set multer destination in api.js in nodejs and call another function? - javascript

i am using a api in server.js file.
app.post('/api/uploadalumniresume', api.UploadAlumniResume);
my api.js file code is:
exports.UploadAlumniResume = (req, res) => {
const storage = multer.diskStorage({
destination(req, file, cb) {
// cb(null, `${__dirname}public/images/uploads`);
cb(null, `${__dirname}/../public/imports`);
},
filename(req, file, cb) {
cb(null, `${Date.now()}-${file.originalname}`);
},
});
const importupload = multer({
storage,
});
const importmulter = importupload.single('file');
};
The server.js as call the api but the exports api not saving the uploading file to the destination folder
And i need to store the upload file on destination follder.
And if the file as uploaded then function need to work on create fuction in the same api.
please give a any solution to me!

Related

Error: ENOENT: no such file or directory Nodejs

I'm trying to upload a file and store it in an uploads folder, but I get this error: no such file or directory
I get the message success in console but I get this error anyway.
POST /auth/register 500 21.023 ms - 260
Error: ENOENT: no such file or directory, open E:\IMPORTANT\INFO-DEV\DEV\ANGULAR NODEJS\API AUTH\uploads\1671534381494.jpeg
Here is my configuration code for upload.
const path = require("path");
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads/");
},
filename: function (req, file, cb) {
const extension = path.extname(file.originalname);
cb(null, Date.now() + extension);
},
});
const upload = multer({
storage: storage,
fileFilter: function (req, file, callback) {
if (
file.mimetype == "image/png" ||
file.mimetype == "image/jpg" ||
file.mimetype == "image/jpeg"
) {
callback(null, true);
console.log("Image téléchargé avec succès"); // success message
} else {
callback(null, false);
console.log("Seulement du fichier de type png, jpg ou jpeg"); // error message
}
},
limits: {
fileSize: 1024 * 1024 * 2,
},
});
module.exports = upload;
I got the same error, but I was able to solve it by getting my current path.
import multer from "multer";
// Set up multer storage options
const storage = multer.diskStorage({
destination: function (req, file, cb) {
console.log("🚀 ~ file: upload.ts:11 ~ file", process.cwd());
cb(null, `${process.cwd()}/src/Images`);
},
filename: function (req, file, cb) {
cb(null, file.fieldname + "-" + Date.now());
},
});
// Create a multer instance with the storage options
const upload = multer({ storage });
export default upload;
The issue is that your uploads folder doesn't exist or you set the path incorrectly.
I don't know where exactly you created uploads folder (and if you created it at all).
So in the destination param you should pass either:
path.join(__dirname, '/uploads') - in case that folder is in the same location where current js file is located.
Or path.join(process.cwd(), '/uploads') - in case if uploads folder is in the root of the project (where you run npm start etc.)
So, in short words you need to make sure folder exists and then make sure the path is correct.
P.S.
Using ../../ syntax should also work, you can try ../uploads or ../../uploads if, for example, that folder is on higher levels of your folders structure.
Change this line
cb(null, './uploads/');
this line
cb(null, "uploads/");
or try
cb(__dirname, "uploads/");
you can try your path name as like
img: {
data: fs.readFileSync(
path.join(__dirname + "../../uploads" + req.file.filename)
),
contentType: "image/png",
},

File not uploading in Express JS using Multer

I am creating API using express JS. Now, I have a router which will be used to upload image using multer.
Here is my router :
const multer = require('multer');
module.exports = (app) => {
const DIR = './public/';
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, DIR);
},
filename: (req, file, cb) => {
cb(null , file.originalname);
}
});
const upload = multer({ storage: storage });
// I have also tried this but not working
// const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('image'), (req, res, next) => {
res.status(201).json({
message: "File uploaded successfully"
});
});
}
Now, from my reactjs app I am calling this router using axios like this :
const headers = {
"Content-Type": "multipart/form-data"
}
const body = new FormData();
body.append('image', this.state.selectedCategoryImage);
axios.post('http://localhost:3000/upload', body, { headers }).then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
});
In above code this.state.selectedCategoryImage is a selected image from html <input> tag.
Now, When I call this api I am getting my response "file uploaded successfully", but I am not able to see my uploaded image anywhere in public directory. My image is not uploading.
Please anyone can help me what's the issue ?
Pass file Object not URL
URL.createObjectURL(file) // it return file url that you can use to show file preview
For upload file, send actual file in axios API as you got from file input
var file = event.target.files[0]; // return actual file
this way it actually send file object.

Custom file name from frontend in Multer

I'm uploading a file using FormData and receiving it server-side using Multer. Everything works as expected, except since I'm using FileSystem API on the front-end (https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry), the files I'm uploading come from sub-directories. Multer seems to only see the filename, even if I explicitly set an alias for the file as I append it to form data (https://developer.mozilla.org/en-US/docs/Web/API/FormData/append). It also seems like Multer performs its logic prior to the rest of my request handler and does not see the parameters I set on the body. How do I get multer to see the full path?
Here is a simplified version of what I currently have setup:
Client (alias represents full name with path, file.name is the base name automatically set by FileSystem API):
function upload(file, alias) {
let url = window.location.origin + '/upload';
let xhr = new XMLHttpRequest();
let formData = new FormData();
xhr.open('POST', url, true);
return new Promise(function (resolve, reject) {
xhr.addEventListener('readystatechange', function(e) {
if (xhr.readyState == 4 && xhr.status == 200) {
resolve(file.name);
}
else if (xhr.readyState == 4 && xhr.status != 200) {
reject(file.name);
}
})
formData.append('file', file, alias || file.name); // this should in theory replace filename, but doesn't
formData.append('alias', alias || file.name); // an extra field that I can't see in multer function at all
xhr.send(formData);
});
}
Server:
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
// neither req nor file seems to contain any hint of the alias here
cb(null, file.originalname);
}
});
const upload = multer({storage: storage});
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.post('/upload', upload.single('file'), function (req, res, next) {
// by this time the file seems to already be on disk with whatever name multer picked
if (req.file) {
res.status(200).end();
} else {
res.status(500).end();
}
});
In order to get this to work, use the preservePath option when configuring multer. The following will work:
const upload = multer({storage: storage, preservePath: true});
However, it's important to note, multer will not create the directories or subdirectories. Those have to be created beforehand. (I tested this too. If directories are created and empty, upload succeeds, however, if directories do not exist, uploads fail).
In their readme, they say:
"Note: You are responsible for creating the directory when providing destination as a function. When passing a string, multer will make sure that the directory is created for you."
A follow-up to that note would be: "you are responsible for creating any sub-directories too".
The relative paths of files uploaded will be accessible in originalname property. So, backend would look like this: (as you had it, but with updated comments)
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
// If you uploaded for example, the directory: myDir/myFile.txt,
// file.originalname *would* be set to that (myDir/myFile.txt)
// and myFile.txt would get saved to uploads/myDir
// *provided that* uploads/myDir already exists.
// (if it doesn't upload will fail)
// /* if( [ uploads/myDir doesn't exist ] ) { mkdir } */
cb(null, file.originalname);
}
});
Helpful tip:
On the front end, I found it easier to test directory / subdirectory upload with: (tested on Chrome latest ok)
<form action="/uploads/multipleFiles" method="post" enctype="multipart/form-data">
<input type="file" name="multiple" webkitdirectory accept="text/*" onchange="console.log(this.files)" />
<input type="text" name="tester" value="uploadTester" />
<input type="submit"/>
</form>
If you want to upload Passport image as a front and back side then pass parameter from frontend like this user:"username" and type:"front" OR type:"back"
then Use it in node side like this
const upload = multer({
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/passport/');
},
filename: function (req, file, cb) {
cb(null, req.body.user+"-"+req.body.type+".jpg");
}
})
});

nodejs multer diskstorage to delete file after saving to disk

I am using multer diskstorage to save a file to disk.
I first save it to the disk and do some operations with the file and then i upload it to remote bucket using another function and lib.
Once the upload is finished, i would like to delete it from the disk.
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload = multer({ storage: storage }).single('file')
and here is how i use it:
app.post('/api/photo', function (req, res) {
upload(req, res, function (err) {
uploadToRemoteBucket(req.file.path)
.then(data => {
// delete from disk first
res.end("UPLOAD COMPLETED!");
})
})
});
how can i use the diskStorage remove function to remove the files in the temp folder?
https://github.com/expressjs/multer/blob/master/storage/disk.js#L54
update:
I have decided to make it modular and put it in another file:
const fileUpload = function(req, res, cb) {
upload(req, res, function (err) {
uploadToRemoteBucket(req.file.path)
.then(data => {
// delete from disk first
res.end("UPLOAD COMPLETED!");
})
})
}
module.exports = { fileUpload };
You don't need to use multer to delete the file and besides _removeFile is a private function that you should not use.
You'd delete the file as you normally would via fs.unlink. So wherever you have access to req.file, you can do the following:
const fs = require('fs')
const { promisify } = require('util')
const unlinkAsync = promisify(fs.unlink)
// ...
const storage = multer.diskStorage({
destination(req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename(req, file, cb) {
cb(null, `${file.fieldname}-${Date.now()}`)
}
})
const upload = multer({ storage: storage }).single('file')
app.post('/api/photo', upload, async (req, res) =>{
// You aren't doing anything with data so no need for the return value
await uploadToRemoteBucket(req.file.path)
// Delete the file like normal
await unlinkAsync(req.file.path)
res.end("UPLOAD COMPLETED!")
})
Multer isn't needed. Just use this code.
const fs = require('fs')
const path = './file.txt'
fs.unlink(path, (err) => {
if (err) {
console.error(err)
return
}
//file removed
})
You may also consider using MemoryStorage for this purpose, with this storage the file is never stored in the disk but in memory and is deleted from the memory automatically after execution comes out of controller block, i.e., after you serve the response in most of the cases.
When you will use this storage option, you won't get the fields file.destination, file.path and file.filename, instead you will get a field file.buffer which as name suggests is a buffer, you can convert this buffer to desired format to do operations on and then upload using a stream object.
Most of the popular libraries support streams so you should be able to use stream to upload your file directly, code for converting buffer to stream:
const Readable = require('stream').Readable;
var stream = new Readable();
stream._read = () => { }
stream.push(file.buffer);
stream.push(null);
// now you can pass this stream object to your upload function
This approach would be more efficient as files will be stored in memory which will result in faster access, but it does have a con as mentioned in multer documentation:
WARNING: Uploading very large files, or relatively small files in
large numbers very quickly, can cause your application to run out of
memory when memory storage is used.
To do it truly automatically across all routes I used this strategy :
when the request ends, we delete all the uploaded files (req.files). Before that, if you want to keep the files on the server, you need to save them in another path.
var express = require('express');
var app = express();
var http = require('http');
var server = http.Server(app);
// classic multer instantiation
var multer = require('multer');
var upload = multer({
storage: multer.diskStorage({
destination: function (req, file, cb) {
cb(null, `${__dirname}/web/uploads/tmp/`);
},
filename: function (req, file, cb) {
cb(null, uniqid() + path.extname(file.originalname));
},
}),
});
app.use(upload.any());
// automatically deletes uploaded files when express finishes the request
app.use(function(req, res, next) {
var writeHead = res.writeHead;
var writeHeadbound = writeHead.bind(res);
res.writeHead = function (statusCode, statusMessage, headers) {
if (req.files) {
for (var file of req.files) {
fs.unlink(file.path, function (err) {
if (err) console.error(err);
});
}
}
writeHeadbound(statusCode, statusMessage, headers);
};
next();
});
// route to upload a file
router.post('/profile/edit', access.isLogged(), async function (req, res, next) {
try {
// we copy uploaded files to a custom folder or the middleware will delete them
for (let file of req.files)
if (file.fieldname == 'picture')
await fs.promises.copy(file.path, `${__dirname}/../uploads/user/photo.jpg`);
} catch (err) {
next(err);
}
});
I have removed directory after file uploaded using fs-extra
const fs = require('fs-extra');
// after you uploaded to bucket
await fs.remove('uploads/abc.png'); // remove upload dir when uploaded bucket

Node JS Create link from picture to database

I want to create a link (./pictures) of a picture which is already uploaded and add the link to my MySQL-DB.
Picture Save:
var Storage = multer.diskStorage({
destination: function(req, file, callback) {
callback(null, "./pictures");
},
filename: function(req, file, callback) {
pictureSaveFormat = Date.now();
file.originalname);
callback(null, pictureSaveFormat + ".jpg");
}
});
This only pseudo/example code but I hope it helps:
const storage = multer.diskStorage({
destination(req, file, cb) { /* save to destination */},
filename(req, file, cb) { /* make filename */ }
});
router.post('/', multer({ storage }), (req, res) => {
// you can store all these params
const fileAttrs = {
fieldname: req.file.fieldname,
originalname: req.file.originalname,
encoding: req.file.encoding,
mimetype: req.file.mimetype,
destination: req.file.destination,
filename: req.file.filename,
path: req.file.path,
size: req.file.size
}
// save fileAttrs into your database (e.g. using Photo model)
Photo.create(fileAttrs).then(() => {
// handle response
res.redirect('/');
});
});
The main idea is that you may want to create the storage middleware and pass it to your route handler. Once an image is uploaded you should have access to a number or attributes that can be stored in the database and used later to retrieve the image.
As for storing the attributes in the database, I'm going to assume that you're using some sort of ORM such bookshelf or sequelize. In which case, you'll have a photo/image model that can be used to write those attrs to the database.
All of that needs to be done in your route handler before handling the response.

Categories