Using : NODEJS, EXPRESS.
So here is the context: I created a form which then transforms the responses into docx with a template (with docx-templater in react).
Then I store my received docx in my mongodb database (works fine).
But here it is, I would also like to store it in an internal folder on the server side.
So I wanted to use multer.
When I "post" my form
The docx is saved on my computer
The docx is saved on my mongodb
But he is not save in my 'public' folder.
I have no error in console.
Here is my controller
router.post(
"/upload",
uploadMiddleware,
uploadMulter.single("myFile"),
async (req, res) => {
console.log(req.file);
const { file } = req;
const { id } = file;
try {
if (file.size > 5000000) {
deleteDocx(id);
return res.status(400).send("file may not exceed 5mb");
}
} catch (err) {
return res.status(201).json(err);
}
return res.send(file.id);
}
);
This is uploadMulter
const multerStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, `./public/docx`);
},
filename: (req, file, cb) => {
cb(null, file.filename + "." + file.mimetype);
},
});
const uploadMulter = multer({
storage: multerStorage,
});
I've tried
To use fileSystem with createWriteStream
To change the path with '/public/docx' or ${__dirname}/public/docx
I've created my public and docx folder so it's not a folder problem.
Related
So i'm building a marketplace mobile app using, expo that let users upload products to the marketplace. I'm having difficult times using the expo FileSystem.FileSystemUploadType(https://docs.expo.dev/versions/latest/sdk/filesystem/#filesystemfilesystemsessiontype) to pick the image and send to the backend.
here's my front-end code
const handleSubmit = async (listing, { resetForm }) => {
const data = new FormData();
data.append("images", listing.images[0]);
data.append("description", "Good product");
console.log( listing.images[0])
// it does console the file uri
// file:///Users/zakisb/Library/Developer/CoreSimulator/Devices/83E12EA5-E8FA-4850-82C1-84021B25450D/data/Containers/Data/Application/6873BF40-26E4-4BD3-834D-F6772448C004/Library/Caches/ExponentExperienceData/%2540anonymous%252Flokazz_app2-5f4724db-b9d7-45aa-a8ca-ac5acf2f4780/ImagePicker/B72CF40C-EC27-430E-B1F8-B983C0ACF2FB.jpg
// i tried this solution first and worked perfectly. but it does upload the image only and i want to send the formdata object
const response = await FileSystem.uploadAsync(
"http://192.168.43.8:5000/products/addProduct",
listing.images[0],
{
fieldName: "images",
uploadType: FileSystem.FileSystemUploadType.MULTIPART,
}
);
// so i tried this but i get an error
const response = await FileSystem.FileSystemUploadType.MULTIPART(
"http://192.168.43.8:5000/products/addProduct",
data
);
};
My backend works perfectly. i tried the api call using insomnia and postman the file gets uploaded successfully to the folder. but using expo methods i get nothing.
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./images");
},
filename: function (req, file, cb) {
cb(null, Date.now() + file.originalname);
},
router.post(
"/products/addProduct",
upload.single("images"),
async (req, res) => {
console.log(req.body);
try {
res.send(req.body);
} catch (err) {
res.send("Error " + err);
}
}
);
});
I have successfully used Multer with NestJs to check for fileTypes so only images are saved. However when a non image is uploaded the fileCheck works great but Nest returns a 500 error to the client. I want it to return an error message to the client.
Other answers on SO show how to do this using Node but I want to know how to do it using Nest. I followed a video tutorial to get this far but now need this extra functionality.
Controller.ts
#Controller("photos")
export class PhotosController {
#Post("upload")
#UseInterceptors(FileInterceptor("photo", {
storage: diskStorage({
destination: './uploads',
filename: editFileName
}),
fileFilter: imageFileFilter
}))
async uploadSingle(#UploadedFile() file) {
console.log(file);
const response = {
originalname: file.originalname,
filename: file.filename,
};
return response;
}
file-upload.utils.ts
export const imageFileFilter = (req, file, callback) => {
if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
return callback(new Error('Only image files are allowed!'), false);
}
callback(null, true);
};
export const editFileName = (req, file, callback) => {
const name = file.originalname.split('.')[0];
const fileExtName = extname(file.originalname);
const randomName = Array(4)
.fill(null)
.map(() => Math.round(Math.random() * 16).toString(16))
.join('');
callback(null, `${name}-${randomName}${fileExtName}`);
};
How can I catch the error in ImageFileFilter if it exists then still return a response to the client?
I have my back-end server in express(node.js) and all apis is running on this server. I also have file-upload mechanism for file-upload api using multer. For file uploading i have created a middleware and in my helper controller i have this
const storage = multer.diskStorage({
destination: (req, file, cb) => {
let path = getFileStoragePath(req, file);
console.log(`path to create ${path}`)
// let path = `uploads/transId${req.body.refrenceId}/transporter`
checkDirectory(path, (err) => {
if (err) {
console.log(`Error occured if checkDirectory ${err.message}`)
cb(err, null)
} else {
cb(null, path);
}
});
},
filename: (req, file, cb) => {
let dateNow = new Date()
cb(null, `${file.fieldname}_${dateformat(dateNow, 'dddd_mmmm_dS_yyyy_h_MM_ss_TT')}${path.extname(file.originalname)}`)
}
});
const saveFilesToFolder = async(req, res, next) => {
const upload = multer({
storage: storage,
fileFilter: imageFilter,
limits: {
fileSize: 1024 * 1024 * 10
}
}).any();
upload(req, res, (err) => {
const wasValidRequest = checkAllowedFiles(req);
if (wasValidRequest.status === false) {
return res.send({
status: false,
message: wasValidRequest.message,
response: null
})
}
// counter = 0
if (err) {
console.log(`Error uploading files, ${err.message}`)
return res.send({
status: false,
message: `Error occurred while uploading files, ${err.message}`,
response: null
})
}
// WHEN FILE UPLOADING IS DONE NOW PASSING THE REQUEST
next();
});
};
And in my route.js file i have attached my middleware to save files into folder and reading files like this
router.post('/upload-files', saveFilesToFolder, catchAsyncErrors(fileController.UploadFiles));
but now my requirement is that i want to read the content of file which is coming in incoming requestwithout saving that file to local disk by accessing the file inside my fileController function and i want to make a separate api for this purpose?
How can i do this
Multer provide memory options by which without storing file in local system, we can convert it into buffer and read the content.
Refer this or this
var storage = multer.memoryStorage();
var upload = multer({ storage: storage });
app.post('/imagenes', upload.single('image_field'), function(req,res){
req.file.buffer;
});
In controller you can use
console.log(String(req.file.buffer))
to look into content
My requirement was to use multer milddleware inside of my controller function to access/read incoming file from request. So i have achieved that and i am giving an answer for future readers so i did like this
controller.js
var storage = multer.memoryStorage({
destination: function(req, file, callback) {
callback(null, '');
}
});
var upload = multer({ storage: storage }).any();
const saveFilesToS3 = async(req, res) => {
upload(req, res, async(err) => {
console.log(req.files[0].buffer) // printing incoming file content as buffer
// rest of the code here
})
}
In cloudinary I have a folder called images, I want to upload the files into that folder.
I have done set up the cloudinary config. The storage options and the file filter has been done. In the request, I send the post request that will upload the file to cloudinary, but not to the folder. How can I upload a file to a certain folder in Cloudinary?
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './images/');
},
filename: (req, file, cb) => {
cb(null, new Date().toISOString().replace(/:/g, '-') + file.originalname);
}
});
const fileFilter = (req, file, cb) => {
if (!file.mimetype.match(/jpe|jpeg|png|gif$i/)) {
cb(new Error('File is not supported'), false);
return;
}
cb(null, true);
};
const upload = multer({
storage,
fileFilter
});
router.post('/', upload.single('profileImage'), async (req,res) => {
const result = await cloudinary.v2.uploader.upload(req.file.path);
})
When you're performing the upload request you can specify a set of options to be used for that upload. In these options, you can specify the 'public_id' (filename) and/or 'folder' of where the file should be stored.
For example, to upload a file to a folder called 'test', you can use the code below:
cloudinary.v2.uploader.upload(
"path/to/file",
{
folder: "test",
},
function(error, result) {
console.log(error,result);
}
);
You can find out all available options for the upload method via this section of the documentation:
https://cloudinary.com/documentation/image_upload_api_reference#upload_method
I am writing a node application and I was looking for something to upload files on the server. I could get files to upload when there was only one static directory. But I need to make directories per user and then upload files to those, according to the user that's logged in. I looked stuff up but everything that I try ends in an Error: ENOENT: no such file or directory, open ... error. What I am trying to do currently is this -
let storage = multer.diskStorage({
destination: function(req, file, cb) {
let dest = path.join(__dirname, './documents', 'somenameigetfromtheuser');
let stat = null;
try {
stat = fs.statSync(dest);
}
catch (err) {
fs.mkdirSync(dest);
}
if (stat && !stat.isDirectory()) {
throw new Error('Directory cannot be created');
}
cb(null, dest);
}
});
let upload = multer({
storage: storage,
dest: 'documents/'
});
app.post('/testUpload', upload.single('testfile'), (req, res) => {
res.json({
test: 'test'
})
});
There is a similar question that has been answered but it doesn't work that way for me because I want the directory name from the request object.
When I remove the storage property in my multer initialization, the files are stored in the documents directory with a random name. I want the file to have its original name and I want it to be stored in a directory where I get the name of the directory from the req object.
Help a brother out, thanks!
edited
See https://github.com/expressjs/multer#diskstorage
Note that req.body might not have been fully populated yet. It depends on the order that the client transmits fields and files to the server.
Due to that, first write file in temp directory, read directory name from req and move file:
fs = require('fs-extra'); //npm install fs.extra
...
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '../tempDir/')
},
filename: function (req, file, cb) {
cb(null, file.originalname)
}
});
var upload = multer({
storage: storage
}).single('file');
upload(req, res, function (err) {
if (err) {
res.json({});
return;
}
var dir = JSON.parse(req.body.data).directory;
var filename = req.file.filename;
fs.move('../tempDir/' + fileName, '../tempDir/' + dir + '/' + fileName, function (err) {
if (err) {
return console.error(err);
}
res.json({});
});
});
Here's code for dynamic path by argument!
exports.upload = (folderName) => {
return imageUpload = multer({
storage: multer.diskStorage({
destination: function (req, file, cb) {
const path = `src/assets/uploads/${folderName}/`;
fs.mkdirSync(path, { recursive: true })
cb(null, path);
},
// By default, multer removes file extensions so let's add them back
filename: function (req, file, cb) {
cb(null, Date.now() + path.extname(file.originalname));
}
}),
limits: { fileSize: 10000000 },
fileFilter: function (req, file, cb) {
if (!file.originalname.match(/\.(jpg|JPG|webp|jpeg|JPEG|png|PNG|gif|GIF|jfif|JFIF)$/)) {
req.fileValidationError = 'Only image files are allowed!';
return cb(null, false);
}
cb(null, true);
}
})
}
and call it
Make sure you append first the textfields on the client-side and only then do you append the files. In my case i had something like this:
`
for(let i=0; i<files.length;i++)
{
formData.append("files[]",files[i]);
}
formData.append("username",username);
`
The fix was to first append the textfield like so:
`
formData.append("username",username);
for(let i=0; i<files.length;i++)
{
formData.append("files[]",files[i]);
}
`
Here's what I do for uploading files to dynamic directories.
In frontend I use URL parameters to pass user IDs.
await axios({
method: 'post',
data: formData,
url: '/api/upload?userId=123',
headers: { 'content-type': 'multipart/form-data' }
})
In backend get that parameter and use for destination. Also create the directory if it doesn't exist.
const upload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => {
const directory = `./public/uploads/${req.query.userId}`
if (!fs.existsSync(directory)) {
fs.mkdirSync(directory, { recursive: true })
}
cb(null, directory)
},
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${file.originalname}`)
}
})
})
In my project I use multer as follow:
1.Store the file first in a common directory, like /tmp/.
2.Copy/move the file anywhere you want, to CDN in my case, and to a user folder in yours.
3.Remove the original file in /tmp if needed.
And maybe let upload = multer({
storage: storage,
dest: 'documents/'
}); you should remove the dest here since you specified dest in storage, right?
const storage = multer.diskStorage({
destination: function(req, file, callback){
callback(null, path.dirname('D:/') + 'Integra Qamba Site/');
},
filename: function(req, file, callback){
let data = new Date();
callback(null, dateTime +".jpg");
}
});