I upload an image from a react native expo app.
In the back end, I have this code :
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, "avatars");
},
filename: (req, file, callback) => {
let imagePath = Date.now() + path.extname(file.originalname);
callback(null, imagePath);
},
});
const upload = multer({ storage: storage });
app.use("/uploadAvatar", upload.single("avatar"), (req, res) => {
res.status(200).json("Image enregistrée !");
});
It works fine and save the image into a folder.
What I need to do, is to save the image path in the database.
The image is an avatar for a user profile, so I need to add its path to the user table.
Suppose you are storing the image in your middleware:
app.use('/uploadAvatar', upload.single('avatar'), async (req, res) => {
// Assume that you're storing the image with a function,
// in this case you just need to pass the filename into
// your function. This is a pseudocode and is not expected to work
// as it is, so please adjust accordingly.
const result = await storeImage(req.file.filename);
// Return the response as JSON.
res.status(200).json('Image enregistrée!');
});
Basically, you have to inspect the req.file that would be populated after you call the upload.single('avatar') middleware. In this case, we're taking the filename to be stored into the database. You can do any path/file/property manipulations that you might want to do before storing that image, though.
For further information about req.file and what properties it contains, please see the Multer documentation.
Related
So I'm new to backend and node, and have been working on an app that should also support an image file upload when creating a new item.
I had issues at first getting this error ENOENT: no such file or directory but after following the answers here (I'm using a windows machine and was following a tutorial on a Mac)
ENOENT: no such file or directory .?
I've switched to using the __dirname together with path and I have no such error anymore.
What I do face now is another issue:
When I ask for the file.path, it is no longer relative like ./uploads but instead it is the full path on my computer
C:\Users\myuser\Documents\Coding\travel-market\api\src\uploads\2022-12-05T12-39-35.924Z-Screenshot 2022-11-02 193712.png
So when I pull that new item and try to render the image it doesn't show. Also I get this error Not allowed to load local resource.
Is that ok and would work just fine once the api is actually hosted on a server? Or is there a different way of doing things that would allow me to also view the image while I'm developing locally?
This is my entire code for saving right now:
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, path.join(__dirname, "..", "uploads"));
},
filename: function(req, file, cb) {
const uniqueName =
new Date().toISOString().replace(/:/g, "-") + "-" + file.originalname;
cb(null, uniqueName);
},
});
const fileFilter = function(req, file, cb) {
if (!file.originalname.match(/\.(jpg|jpeg|png)$/)) {
return cb(new Error("Please upload an image file"));
}
cb(undefined, true);
};
const upload = multer({
storage,
limits: {
fileSize: 1024 * 1024 * 5, // This number is in bytes, so this is 5mb
},
fileFilter,
});
// Post a new plan
router.post("/plans", auth, upload.single("plan_image"), async(req, res) => {
console.log("this is the file", req.file);
const details = JSON.parse(req.body.details);
console.log("this is the body", details);
const plan = new Plan({
...details,
image: req.file.path,
author: req.user._id,
});
try {
await plan.save();
res.status(201).send(plan);
} catch (e) {
console.log("failed to save", e);
res.status(400).send(e);
}
});
here is a part of my code of multer using nodejs
const storageEngine = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './Images')
const Files = [file.originalname]
console.log(Files)
},
filename: (req, file, cb) => {
cb(null, file.originalname)
}
})
the console log of the above code is this:
['image.png']
['image2.png']
['image3.png]
so i want all file names to be in one array to be posted in mysql in one go as i do not want to send multiple request to mysql as there are numerous amount of images.
so all i want is the file names to be present in one single array like this:
['image.png', 'image2.png', 'image3.png']
The destination event that you use is invoked once per uploaded file, so you cannot use it to collect all file names.
Assuming you have an input field <input type="file" multiple name="upload">, you can instead use
app.use(storageEngine.array("upload"))
.use(function(req, res, next) {
console.log(req.files.map(file => file.originalname));
next();
});
Attaching Documents Pic
As shown in the pic i am attaching my documents.
onDrop: (acceptedFiles, rejectedFiles) => {
if (rejectedFiles.length) {
toast.error('You can upload images , Words and Excel files !.')
} else {
console.log(acceptedFiles)
setFiles([...files, ...acceptedFiles.map(file => Object.assign(file))])
}
}
axios.post('http://localhost:5000/AddEmployee', {
user, employeeDocuments : user[2].documents
})
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
I am storing all test documents in the employeedocuments array and then i am sending post request to backend.
employeeDocuments array in console log view
Here i am console log the employeeDocuments array it is looking fine.
enter code here
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./public/uploads");
},
filename: function (req, file, cb) {
cb(null, file.fieldname + "-" + Date.now() + path.extname(file.originalname));
},
});
var upload = multer({ storage: storage });
var uploadMultiple = upload.fields([{ name: 'employeeDocuments', maxCount: 10 }])
This is my backend multer code to upload files i have used the name employeeDocuments to upload files but multer is not uploading these files.Rather i used postman and it was working fine. But when i upload these files using react by post request these files not get uploaded.
I faced a similar issue and I was able to fix it by modifying my axios post request to sending Form Data:
const formData = new FormData()
formData.append('imagePath', image)
The have the upload in the post like this: router.post("/AddEmployee",upload, async (req, res)
And store it in the DB as a path using req.file.path
Sorry if its a bit confusing but this is everything I have in my code and its fully working
So I'm trying to make the html form:
<form action="blahblah" encblah="multipart/form-data" whatever>
Thats not the problem, I need to make that form send the blob to express
app.post('/upload/avatars', async (req, res) => {
const body = req.body;
console.log(req.file);
console.log(body);
res.send(body);
});
So I can access the blob, create a read stream, pipe it to the cloud, and bam, upload the file without downloading anything on the express server it self.
Is that possible?
If yes, please tell me how.
If no, please tell me other alternatives.
On the client we do a basic multi-part form upload. This example is setup for a single image but you could call uploadFile in sequence for each image.
//client.ts
const uploadFile = (file: File | Blob) => {
const formData = new FormData();
formData.append("image", file);
return fetch("/upload", {
method: "post",
body: formData,
});
};
const handleUpload = (event: any) => {
return event.target.files.length ? uploadFile(event.target.files[0]) : null;
};
On the server we can use multer to read the file without persisting it to disk.
//server.js
const express = require("express");
const app = express();
const multer = require("multer");
const upload = multer();
app.post(
"/upload",
upload.fields([{ name: "image", maxCount: 1 }]),
(req, res, next) => {
console.log("/upload", req.files);
if (req.files.image.length) {
const image = req.files.image[0]; // { buffer, originalname, size, ...}
// Pipe the image.buffer where you want.
res.send({ success: true, count: req.files.image.originalname });
} else {
res.send({ success: false, message: "No files sent." });
}
}
);
For larger uploads I recommend socket.io, but this method works for reasonably sized images.
it is possible, but when you have a lot of traffic it would overwhelm your express server (in case you are uploading videos or big files ) but if it's for uploading small images (profile image, etc...) you're fine. either way you can use Multer npm
I'd recommend using client-side uploading on ex: s3-bucket, etc..., which returned a link, and therefore using that link.
I am making a multer storage engine which makes stream connection between client and S3 Server.
At middle of the stream, my code examine chunks and send it to S3.
I could get a file stream from node.js server. But when I request file array upload, node inspector shows only one stream. What should I do?
Stream engine snippet
CustomStreamEngine.prototype._handleFile = function _handleFile (req, file, cb) {
// for inspect
req.files.length // 1
file;
};
request controller
var streamStorage = multer({
storage: streamEngine()
});
dev.post('/rec_test', streamStorage.array('source'), (req, res, next) => {
});
I just published this streaming multipart/form-data parser on npm as form-parser:
You should be able to do the following:
dev.post('/rec_test', async (req, res, next) => {
// Parse request
await parser(req, async ({ fieldType, fieldName, fieldContent }) => {
// Log all fields
console.log({ fieldType, fieldName, fieldContent });
// Handle 'source' file fields
if (fieldType === 'file' && fieldName === 'source[]') {
// Get file info
const { fileName, fileType, fileStream } = fieldContent;
// Upload fileStream to S3 :-)
}
});
});
Hope it's helpful.
K
I think you can add some logs to https://github.com/expressjs/multer/blob/master/lib/make-middleware.js to check.
Currently, I use axios on the client to send multi files to the server with multer. And I can see all files in the function
busboy.on('file', function (fieldname, fileStream, filename, encoding, mimetype), but there is only one file at a time, and this function will call the _handfile function of the custom storage, so that I think it is the reason for your issue.
Hope it can help you