Reputation: 101
How do I get uploaded image in next.js API route and save it on public folder? I have front end ready. I'm uploading images to an endpoint using plain JavaScript. here is the onSubmit function for uploading images. Suggest me if I'm doing it wrong here. The main question is how do I retrieve it?
const onSubmit=async(e)=>{
e.preventDefault();
const fd=new FormData()
fd.append('myfile',image.name)
let res=await fetch(`http://localhost:3000/api/upload`,{
method: 'POST',
headers: {
"Content-Type": "image/jpeg",
},
body: fd,
})
let response=await res.json();
one more bonus question, it's surely not a good idea to save the uploaded images on public folder. I have save it somewhere on the cloud.
Upvotes: 8
Views: 47336
Reputation: 261
NextJS 13+ is perfectly capable of handling form data and image upload by its own. You don't need formidable, multer, etc... You can easily save images to your local directory with the below code.
import { NextResponse } from "next/server";
import path from "path";
import { writeFile } from "fs/promises";
export const POST = async (req, res) => {
const formData = await req.formData();
const file = formData.get("file");
if (!file) {
return NextResponse.json({ error: "No files received." }, { status: 400 });
}
const buffer = Buffer.from(await file.arrayBuffer());
const filename = Date.now() + file.name.replaceAll(" ", "_");
console.log(filename);
try {
await writeFile(
path.join(process.cwd(), "public/uploads/" + filename),
buffer
);
return NextResponse.json({ Message: "Success", status: 201 });
} catch (error) {
console.log("Error occured ", error);
return NextResponse.json({ Message: "Failed", status: 500 });
}
};
Upvotes: 25
Reputation: 3
/pages/api/createpost
Using for npm i formidable
nextjs javascript and mongodb
. I have used mongoose
to create my models
import { IncomingForm, File } from 'formidable';
import * as fs from "fs";
import path from "path";
import { v4 as uuidv4 } from 'uuid';
import Post from '../../../models/Model';
export const config = {
api: {
bodyParser: false,
},
};
export default async function handler(req, res) {
if (req.method !== 'POST') {
return;
}
// Parse incoming form data using a Promise
try {
const data = await new Promise((resolve, reject) => {
const form = new IncomingForm();
form. Parse(req, (err, fields, files) => {
if (err) return reject(err);
resolve({ fields, files });
});
});
// Define the folder path for storing images
const publicFolderPath = path.Join(process.cwd(), 'public', "images");
let responseData;
// Check if an image file was uploaded
if (data.files.image) {
const oldPath = data.files.image[0].filepath;
const newFileName = new Date().getTime() + "-" + uuidv4() + "-" + data.files.image[0].originalFilename;
const newPath = path.join(publicFolderPath, newFileName);
try {
// Copy the uploaded image to the designated path
await fs.promises.copyFile(oldPath, newPath);
console.log('File copied to:', newPath);
console.log('File uploaded and renamed:', newFileName);
// Create an object with form data
const formData = {
banner: data.fields.banner[0],
body: data.fields.body[0],
image: newFileName,
author: data.fields.author[0],
};
// Create a new post entry in the mongodb database
try {
const post = await Post.create(formData);
responseData = post;
} catch (err) {
console.log(err.message);
}
} catch (error) {
console. Error('Error renaming/moving file:', error);
res.status(500).json({ error: 'Error processing uploaded file.' });
return;
}
} else {
responseData = data;
}
// Respond with the processed data
res.status(200).json(responseData);
} catch (error) {
console. Error('Error parsing form data:', error);
res.status(500).json({ error: 'Error processing form data.' });
}
}
Upvotes: 0
Reputation: 5383
I suggest the popular and lightweight formidable
library:
# install
yarn add formidable@v3 @types/formidable
// pages/api/file-upload.ts
import fs from "fs";
import path from "path";
import { File } from "formidable";
// Important for NextJS!
export const config = {
api: {
bodyParser: false,
},
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<string>
) {
try {
// Parse request with formidable
const { fields, files } = await parseFormAsync(req);
// Files are always arrays (formidable v3+)
const myfile = (files["myfile"] as any as File[])[0];
// Save file in the public folder
saveFile(myfile, "./public/uploads");
// Return success
res.status(200).json("success!");
} catch (e) {
return res.status(500).json(e);
}
}
function saveFile(file: File, publicFolder: string): void {
const fileExt = path.extname(file.originalFilename || "");
fs.renameSync(file.filepath, `${publicFolder}/${file.newFilename}${fileExt}`);
}
// ./helpers/formidable.ts
import type { NextApiRequest } from "next";
import formidable from "formidable";
export type FormidableParseReturn = {
fields: formidable.Fields;
files: formidable.Files;
};
export async function parseFormAsync(
req: NextApiRequest,
formidableOptions?: formidable.Options
): Promise<FormidableParseReturn> {
const form = formidable(formidableOptions);
return await new Promise<FormidableParseReturn>((resolve, reject) => {
form.parse(req, async (err, fields, files) => {
if (err) {
reject(err);
}
resolve({ fields, files });
});
});
}
one more bonus question, it's surely not a good idea to save the uploaded images on public folder. I have save it somewhere on the cloud.
You can save on cloud services with Formidable.
See the official examples: https://github.com/node-formidable/formidable/blob/master/examples/store-files-on-s3.js
But you don't need to use cloud storage to protect private uploads. You can store them locally.
/private-uploads/{logged_user_id}/
;https://.../uploads/{filename}
..
and similar on the filename
to obtain unauthorized access;filename
having this in mind (ex. only allow alphanumeric characters);Upvotes: 1
Reputation: 459
This is the endpoint code I used for uploading image in nextjs, it requires some additional packages I will list them bellow also.
import nextConnect from "next-connect";
import multer from "multer";
import { v4 as uuidv4 } from "uuid";
let filename = uuidv4() + "-" + new Date().getTime();
const upload = multer({
storage: multer.diskStorage({
destination: "./public/uploads/profiles", // destination folder
filename: (req, file, cb) => cb(null, getFileName(file)),
}),
});
const getFileName = (file) => {
filename +=
"." +
file.originalname.substring(
file.originalname.lastIndexOf(".") + 1,
file.originalname.length
);
return filename;
};
const apiRoute = nextConnect({
onError(error, req, res) {
res
.status(501)
.json({ error: `Sorry something Happened! ${error.message}` });
},
onNoMatch(req, res) {
res.status(405).json({ error: `Method '${req.method}' Not Allowed` });
},
});
apiRoute.use(upload.array("file")); // attribute name you are sending the file by
apiRoute.post((req, res) => {
res.status(200).json({ data: `/uploads/profiles/${filename}` }); // response
});
export default apiRoute;
export const config = {
api: {
bodyParser: false, // Disallow body parsing, consume as stream
},
};
Upvotes: 8