title: 2021112-upload-large-file date: 2023-11-12 tags:
- nextjs
- file
- frontend
upload api
import { connectToDb, fileExists } from "@/lib/mongodb";
import { NextResponse } from "next/server";
import { Readable } from "stream";
import formidable, { errors as formidableErrors } from 'formidable';
var fs = require('fs');
var md5 = require('md5');
export const config = {
api: {
bodyParser: false,
}
};
type ProcessedFiles = Array<[string, File]>;
export default async function handler(req: any, res: any) {
const { bucket } = await connectToDb();
// get the form data
// const data = await req.formData();
// Access uploaded files directly using req.files
//const files = Array.from(req.files.entries());
let status = 200,
resultBody = { status: 'ok', message: 'Files were uploaded successfully' };
const form = formidable({ uploadDir: "/tmp" });
let fields;
let files;
try {
form.parse(req, async (err, fields, files) => {
console.log('fields:', fields);
console.log('files:', files);
for (const [key, value] of Object.entries(files)) {
const isFile = typeof value == "object";
if (isFile) {
let file: any = value[0]
let filename = file.originalFilename
let type = file.mimetype
let buffer = fs.readFileSync(file.filepath);
const stream = Readable.from(buffer);
const hash = md5(buffer)
const existing = await fileExists(hash);
if (existing) {
// If file already exists, let's skip it.
// If you want a different behavior such as override, modify this part.
continue;
}
const uploadStream = bucket.openUploadStream(filename, {
// make sure to add content type so that it will be easier to set later.
contentType: type,
metadata: {
hash:hash
}, //add your metadata here if any
});
// pipe the readable stream to a writeable stream to save it to the database
await stream.pipe(uploadStream);
res.status(200).json({
hash: hash
})
}
}
});
} catch (err: any) {
// example to check for a very specific error
if (err.code === formidableErrors.maxFieldsExceeded) {
}
console.error(err);
res.writeHead(err.httpCode || 400, { 'Content-Type': 'text/plain' });
res.end(String(err));
return;
}
return res.json({ success: true });
}
response image
import { MongoClient, ObjectId, GridFSBucket } from 'mongodb';
import clientPromise, { connectToDb } from "@/lib/mongodb";
export default async function handler(req:any, res:any) {
const client = await clientPromise;
const {hash} = req.query;
const db = client.db("fanstick");
const metadata:any = await db
.collection('media.files')
.find({"metadata.hash":hash})
.toArray()
console.log(metadata)
const {bucket}= await connectToDb()
res.writeHead(200, { 'Content-Type': metadata[0].contentType });
bucket.openDownloadStream(metadata[0]._id)
.on('data', (chunk) => {
res.write(chunk);
})
.on('end', () => {
res.end();
})
.on('error', (err) => {
res.status(500).json({ error: err.message });
});
// const file:any = await db
// .collection('media.chunks')
// .find({"files_id":metadata[0]._id})
// .toArray()
// console.log(file[0].data)
// res.setHeader('Content-Type', metadata[0].contentType)
// res.send(file[0].data)
}
Frontend
fetch(`/backend/upload/file`, {
method: "POST",
body: formData
}).then((res) => res.json())
.then(({hash}) => {
const image_url = `${process.env.Deploy_URL}/api/file/${hash}`
setImage(image_url)
form.setFieldValue(field.name,image_url)
})
Ref
- https://github.com/gapon2401/upload-files-nextjs/blob/master/pages/api/upload.ts
- https://reacthustle.com/blog/how-to-upload-retrieve-images-to-mongodb-using-nextjs-13-app-router