I have been debugging this all day and am finally out of googling options.
I have a Vue3 frontend that receives an image and is supposed to send said image to the backend. I have verified that the image is, in fact, being sent, but the file object in Multer is showing undefined every time regardless of what the front-end / network says is being sent.
Here's my code
Frontend
<template>
<div class="flex justify-center">
<div class="mb-3 w-96">
<label for="formFile" class="form-label inline-block mb-2 text-gray-700"
>Upload a new profile picture</label
>
<input
class="form-control block w-full px-3 py-1.5 text-base font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 rounded transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none"
type="file"
@change="onSelect"
id="formFile"
/>
<span class="message"> {{ message }} </span>
</div>
</div>
</template>
<script>
import axios from "redaxios";
export default {
name: "FileUpload",
props: {
user: Array | Object,
},
created() {},
data() {
return {
file: "",
message: "",
};
},
methods: {
onSelect(e) {
this.file = e.target.files[0] || e.dataTransfer.files;
if (this.file.length) return;
const formData = new FormData();
console.log(this.file);
formData.append("id", this.user.id);
formData.append("file", this.file);
//formData.set("enctype", "multipart/form-data");
console.log(formData);
try {
axios
.post("http://localhost:3669/uploadPhoto", formData)
.then((res) => {
this.message = "Uploaded successfully!";
});
} catch (err) {
console.log(err);
this.message = "Something went wrong uploading your photo!";
}
},
},
};
</script>
Backend
const fileFilter = (req: express.Request, file: File, cb) => {
const allowedTypes = /jpeg|jpg|png|gif/;
const extname = allowedTypes.test(path.extname(file.name).toLowerCase());
const mimetype = allowedTypes.test(file.type);
if (!mimetype && extname) {
const error = new Error("Incorrect Filetype");
error.name = "INCORRECT_FILETYPE";
return cb(error, false);
}
cb(null, true);
}
const storage = multer.diskStorage({
destination: function (req, file, cb) {
if (req.body.id) {
cb(null, `./public/photos/${req.body.id}`)
} else {
cb(null, "./public/photos");
}
},
filename: function (req, file: File, cb) {
console.log(req.file);
cb(null, file.name + ".png");
},
});
const imageUpload : Multer = multer({
storage,
limits: {
fileSize: 5000000
}
});
app.post('/uploadPhoto', imageUpload.single('file'), async (req: express.Request, res:express.Response) => {
console.log(req.body);
console.log(req.file);
res.json({ message: 'Successfully uploaded file' });
return;
});
I omitted some code for space reasons, but for some reason it just won't work. Logged file returns a file, but the backend refuses to see it.
Thanks
It seems your Multer DiskStorage configuration is the cause of the problems.
My guess is you have the wrong file paths because ./
is not what you think. This is why it's always good to resolve paths from __dirname
(the current script's parent directory).
Note that the documentation for destination
states...
If a string is passed and the directory does not exist, Multer attempts to create it recursively
So you'll need to manually create the upload directories since you're using the function version.
You also have the wrong argument type in your filename
hook. The file
argument is of type Express.Multer.File
, not File
. You don't need to set this as it's already defined by the function signature.
import { resolve } from "path";
import { mkdir } from "fs/promises";
import multer from "multer";
const uploadBase = resolve(__dirname, "./public/photos");
const storage = multer.diskStorage({
destination: async (req, _file, cb) => {
const path = req.body.id ? resolve(uploadBase, req.body.id) : uploadBase;
try {
// create the directory
await mkdir(path, { recursive: true });
cb(null, path);
} catch (err) {
cb(err);
}
},
filename: (_req, file, cb) => {
cb(null, file.originalname); // note the property used
}
});
Make sure you install @types/multer
as a dev-dependency to get useful linting in your editor.