Im trying to single file upload to folder public/files, using reactJS as frontend, multer nodeJS as serverside :
react :
here i have file input that accepts only pdf and msword, and on change it sets the file state, on submit it triggers function handleCareer which makes formData object and appends data from state, the file.name will be something like 1663010450031report.pdf, and then make request to /upload.
const [file, setFile] = useState(null);
const handleCareer = async () => {
const data = new FormData();
const pdf = `${Date.now()}${file.name}`;
data.append("file", pdf);
try {
await fetch("http://localhost:8000/upload", {
method: "POST",
body: data,
});
} catch (err) {
console.log(err);
}
};
<div className="uploadCV">
<label htmlFor="resumeFile" className="downloadImg">
<AddAPhotoIcon className="postIcon" />
<span>Upload resume</span>
</label>
<input
type="file"
accept="application/pdf,application/msword"
id="resumeFile"
name="file"
onChange={(e) => setFile(e.target.files[0])}
></input>
</div>
<div className="submitUpload">
<button type="button" onClick={handleCareer}>
Request
</button>
</div>
server :
here im using path public/files as the target folder for upload and specifying in destination as well as specifying req.body.file for filename
app.use("/files", express.static(path.join(__dirname, "public/files")));
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "public/files");
},
filename: (req, file, cb) => {
cb(null, req.body.file);
},
});
const upload = multer({ storage: storage }).single("file");
app.post("/upload", function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
console.log(err);
} else if (err) {
console.log(err);
}
return res.status(200).send(req.file);
});
});
but the file is not uploading, and it doesn't console log any errors, on postman it gives empty result with status code 200 .. what's is the problem here ?
You haven't send the file field to backend. You have to send a file (like pdf). Now you are sending only a pdf name. There aren't any file.
const upload = multer({ storage: storage }).single("file");
Here you have defined the name of incoming data is file. So you should add the field which named file
Related
how can i upload multiple images to amazon s3
this is my middleware for image upload
const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");
aws.config.update({
secretAccessKey: process.env.AWSSecretKey,
accessKeyId: process.env.AWSAccessKeyId
});
const s3 = new aws.S3();
const upload = multer({
storage: multerS3({
s3: s3,
bucket: 'ajibade',
acl: 'public-read',
metadata: (req, file, cb) => {
cb(null, { fieldName: file.fieldname });
},
key: (req, file, cb) => {
cb(null, Date.now().toString())
}
})
})
module.exports = upload;
this is how i upload the file
<label class="choosefile-button">
<input
type="file"
#change="onFileSelected1"
>
<p style="margin-top: -70px">{{ fileName }}</p>
</label>
<label class="choosefile-button">
<input
type="file"
#change="onFileSelected2"
>
<p style="margin-top: -70px">{{ fileName }}</p>
</label>
how i call the function
methods: {
onFileSelected1(event) {
this.selectedFile = event.target.files[0]
console.log(this.selectedFile)
this.fileName = event.target.files[0].name
},
onFileSelected2(event) {
this.selectedFile = event.target.files[0]
console.log(this.selectedFile)
this.fileName = event.target.files[0].name
},
}
each time i click on either onFileSelected1 or onFileSeleted2 it uploads the same images for the both inputs
this is my post request
router.post(`/products`, upload.single("photo"), async (req, res) => {
console.log(res);
try {
let product = new Product();
product.photo = req.file.location;
await product.save();
} catch (error) {
console.log(error);
}
});
please how can i go about this
This blog post explains quite in depth how it's done.
Basically what you need to do is add a <input type="file" multiple /> and in your upload function turn it into multipart/form-data. Then when you upload it to your backend, you use multer and multer-s3 to send it to S3.
I think you've mixed up a few concepts here, your upload function looks like a route that is supposed to be in the backend of the application, where you then send the request to upload files using for example axios or the fetch API.
My goal is to create a File Select interface in order to upload files on a server using Angular CLI and Node JS.
I built an API with Node JS that must check the type of the file (only CSV files) and then return a JSON text saying whether the upload succeeds or fails. I decided to give it a try with Multer (see https://www.npmjs.com/package/multer).
Everything is working and my CSV is well uploaded. But I don't manage to get the JSON message from the API after a user uploads his file.
Here is the files I created :
HTML PART
<div class="file-upload">
<input type="file" name="file" ng2FileSelect [uploader]="uploader" accept="text/csv" />
<button type="button" class="btn btn-primary btn-sm" (click)="uploader.uploadAll()" [disabled]="!uploader.getNotUploadedItems().length">
RUN
</button>
</div>
TS PART
const URL = 'https://xxxxx.com:xxxx/upload';
export class SelectFileComponent implements OnInit {
public uploader: FileUploader = new FileUploader({
url: URL,
itemAlias: 'fileToUpload',
});
constructor() { }
ngOnInit() {
this.uploader.onAfterAddingFile = (file) => {
file.withCredentials = false;
};
this.uploader.onCompleteItem = (item: any, status: any) => {
};
}
NODE JS PART
// File upload settings
const PATH = './uploads';
let storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, PATH);
},
filename: (req, file, cb) => {
cb(null, file.originalname)
}
});
let upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {
if (file.mimetype == 'application/vnd.ms-excel'){
cb(null, true);
} else {
cb(null, false);
return cb('Only csv format allowed!');
}
}
});
// Post file
app.post('/upload', upload.single('fileToUpload'), async (req, res) => {
// I would like to do :
// if file has not csv extension...
{
return res.json({
success: false,
message: "Bad format, please select a CSV file",
});
}
// else if everything is ok...
{
return res.json({
success: true,
message: "Some success message"
});
}
else {}
});
It seems I can't use a variable created in the "File upload settings" into the "Post file" part.
Have you any ideas to help me to implement that JSON message ?
i made this function, which takes the file value ( image ) from the file state and send it to upload route, through FormData object
const [file, setFile] = useState(null);
const submitPost = async (e) => {
...
e.preventDefault();
if (file) {
const data = new FormData();
const fileName = `${Date.now()}${file.name}`;
data.append("name", fileName);
data.append("file", file);
try {
await fetch("http://localhost:8000/upload", {
headers: {
"Content-type": "application/json",
},
method: "POST",
body: JSON.stringify(data),
});
// window.location.reload();
} catch (err) {
console.log(err);
}
}
};
the file value in the file state is coming from form file input
<form className="postOptions" onSubmit={submitPost}>
<div className="postAreaBot">
<label htmlFor="file" className="downloadImg">
<AddAPhotoIcon className="postIcon" />
<span>Image</span>
</label>
<input
type="file"
accept=".png,.jpeg,.jpg"
onChange={(e) => setFile(e.target.files[0])}
id="file"
style={{ display: "none" }}
></input>
</div>
<button type="submit">Post</button>
</form>
and then sending the file value to this route for upload on folder images inside folder public using multer and path
app.use("/images", express.static(path.join(__dirname, "public/images")));
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "public/images");
},
filename: (req, file, cb) => {
cb(null, req.body.name);
},
});
const upload = multer({ storage });
app.post("/upload", upload.single("file"), (req, res) => {
try {
return res.status(200).json("File uploaded successfully");
} catch (err) {
console.log(err);
}
});
but the image is not being uploaded
what have i tried
i tested the route using file.originalName for postman instead of file.req.body and the route does indeed works and images are being uploaded successfully, i also checked the values name and file in the data object and it appends it successfully, i can't see what is the problem, why it doesn't upload the image file through the react fetch request?
Just remove the JSON.stringify. And change your content type, like the example below:
await fetch("http://localhost:8000/upload", {
headers: {
"Content-type": "multipart/form-data",
},
method: "POST",
body: data,
});
I'm trying to create a small social network in which we can send posts (with or without images).
I manage to create posts without an image (just text), it works very well, but as soon as I add an image to my form and submit the form, it is impossible to find the image. Normally it should be saved in the "images" folder but it is always empty.
I am using multer to do that, here is my code :
My form component :
const WhatsUpForm = ({ className, id, name, placeholder }) => {
const [inputValue, setInputValue] = useState("");
const inputHandler = (e) => {
setInputValue(e.target.value);
};
const submitHandler = async (e) => {
e.preventDefault();
const post = {
author_firstname: JSON.parse(localStorage.getItem("user")).user_firstname,
author_lastname: JSON.parse(localStorage.getItem("user")).user_lastname,
message: inputValue,
date_creation: dayjs().format(),
image_url: ""
};
// POST request
await POST(ENDPOINTS.CREATE_POST, post);
// document.location.reload()
};
return (
<form className={className} onSubmit={submitHandler} method="POST" action="/api/post" enctype="multipart/form-data">
<input className="testt" type="text" id={id} name={name} placeholder={placeholder} required value={inputValue} onChange={inputHandler}/>
<div className="icons_container">
<input type="file" name="image" id="image" className="icons_container__add_file" />
<label for="image">
<FontAwesomeIcon icon={faImages} />
</label>
<button type="submit" className="icons_container__submit">
<FontAwesomeIcon icon={faPaperPlane} />
</button>
</div>
</form>
);
};
My routes and the multer's code :
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, "../images");
},
filename: (req, file, callback) => {
console.log("multer");
console.log("file :", file);
callback(null, Date.now() + path.extname(file.originalname));
},
});
const upload = multer({ storage: storage });
// Post CRUD
router.get("/", auth, postCtrl.getAllPosts);
router.post("/", auth, upload.single("image"), postCtrl.createPost);
router.delete("/:id", auth, postCtrl.deletePost);
router.put("/:id", auth, postCtrl.updatePost);
console.log("multer") is not trigger, and when i look the payload in network tab in my browser, i don't see any images.
And finally, my controller for createPost function :
exports.createPost = (req, res, next) => {
let { body } = req;
delete(req.body.image_url)
body = {
...body,
likes: "",
};
const sql = "INSERT INTO posts SET ?";
db.query(sql, body, (err, result) => {
if (err) {
res.status(404).json({ err });
throw err;
}
res.status(200).json({ msg: "Post added..." });
});
};
For now, i don't want to put the image's URL in my SQL DB, i just want to save the image in my images folders. I have verified the path (../images) and it's coorect.
How do I save the image in my image folder?
I don't see the file data gets sent to server from your POST request.
// object post doesn't have the file data
await POST(ENDPOINTS.CREATE_POST, post);
Consider using FormData
const submitHandler = async (e) => {
e.preventDefault();
const post = new FormData();
// non form data
formData.append("author_firstname", JSON.parse(localStorage.getItem("user")).user_firstname);
...
// form data
formData.append("image", document.getElementById("image").files[0]);
...
// POST request
await POST(ENDPOINTS.CREATE_POST, post);
// document.location.reload()
};
I get undefined when I try to read the req.files and if I read req.body I get file: [object Object] which, when read on the front end before being sent is
Icon: File(4076381)
lastModified: 1513292207750
lastModifiedDate: Thu Dec 14 2017 17:56:47 GMT-0500 (EST) {}
name: "image.jpeg"
size: 4076381
type: "image/jpeg"
webkitRelativePath: ""
but after
let upload = new FormData();
upload.append('file', user);
console.log(upload);
upload logs FormData = {} so I have no idea why .append() did not work. user is the Icon object shown above.
but when sent back it is the same [object Object] as on the server of course.
I am trying to understand why req.file is not readable or any other alternative for how to read the uploaded file so that I can then do whatever I want with it (end game, store in a db as a field in mongodb, then pull out and render on the client side after logging in).
Relevant code
Register.js
// standard React stuff
handleUpload = (e) => {
console.log(e.target.files[0]);
this.setState({ Icon: e.target.files[0] });
};
handleSubmit = async (e) => {
e.preventDefault();
const dispatchObject = {
Icon: this.state.Icon
}
await this.dispatchSubmit(dispatchObject);
}
render(){
return (
<form noValidate autoComplete="off" onSubmit={this.handleSubmit}>
/>
<Input
id='Icon'
type='file'
name='Icon'
accept='image/png, image/jpeg'
onChange={this.handleUpload}
/>
<input type='submit' value='Submit'/>
</form>
)
}
action/index.js
export const register = (user, history) => {
console.log(user);
let upload = new FormData();
upload.append('file', user);
return dispatch => {
axios.post('http://localhost:9000/register', upload)
.then((res) => {
console.log(res.data);
console.log('User successfully created');
})
.catch(err => console.log(err) );
}
}
server.js
import multer from 'multer';
const storage = multer.diskStorage({
destination: './files',
filename(req, file, cb) {
console.log(file);
cb(null, `${new Date()}-${file.originalname}`);
},
});
const upload = multer({ storage });
app.post('/register', upload.single('file'), async (req, res) => {
console.log(req.body.file);
res.json(req.body.file);
});
Multer will create the relevant folder called ./file but there is never anything in it and req.file is not accessible. I cannnot send it through the body in a standard manner because it comes out in the [object Object] format.
you are logging req.body.file but it is req.file.
The solution was too change the below to user.Icon.
export const register = (user, history) => {
console.log(user);
let upload = new FormData();
upload.append('file', user);
I also separated any other files that I wanted to send in another api call.