Multer worked fine when I upload multiple images by using Postman but when I try to send files as an array from the front-end, it always return files is undefined and files is not iterable.
I think the problem is in the front-end React. How to fix this problem?
Back-end
router.post('/multer',auth ,upload.array('image'), async (req, res) => { //NOTE image is the field name
try {
const urls = []
const files = req.files;
console.log('Upload cloudinary running '+ files)
.
.
.
Front-end React
import React, {Fragment, useState, useEffect} from 'react';
const Dashboard = ({auth: { user, loading }}) => {
.
.
.
const [files, setFiles] = useState([]);
const handleChange = e => {
const file_reader = new FileReader();
const file = e.target.files[0];
let file = e.target.files[0];
file_reader.onload = () => {
setFiles([...files, { image: file_reader.result }]);
};
file_reader.readAsDataURL(file);
}
const handleSubbmitFile = (e) => {
e.preventDefault();
if(!files) return;
uploadImage(files);
}
const uploadImage = async (base64EncodedImage) => {
try {
const config = {
headers:{
'Content-Type' : 'application/json'
}
}
const body = JSON.stringify({files: base64EncodedImage});
await axios.post('/api/blogs/multer', body, config);
} catch (error) {
console.log(error);
}
}
return(
<form onSubmit={handleSubbmitFile} className="form-outline">
<input name="image" onChange={handleChange} type="file"
class="form-control-file" id="exampleFormControlFile1"
accept="image/*"/>
</form>
)
}
I found the problems and I fixed it by using formData in handleSubbmitFile and set the property filed to image by using append.
const handleSubbmitFile = (e) => {
e.preventDefault(); //NOTE prevent from reload the page
let formData = new FormData();
for(var i = 0 ; i < files.length; i++){
formData.append('image',files[i])
}
if(!files) return;
uploadImage(formData);
}
Another problems is that using Json.stringify() before using axios.
I didn't use Json.stringify() to convert formData before sending it to back-end via axios. As a result, Multer work well without problems
const uploadImage = async (formData) => {
try {
const config = {
headers:{
'Content-Type' : 'application/json'
}
}
await axios.post('/api/blogs/multer', formData, config);
} catch (error) {
console.log(error);
}
}
Related
Hello everyone I am developing a website using React.js and Node.js where I generate a word document from the user's input and thats worked just fine with docxtemplater!
The problem is I want to let the user download the word document direclty as a PDF document but docxtemplater just permit us to save the file as a docx.
To convert the docx, i have the idea to save my blob document in mongodb with gridfs (already done and worked fine) and then get my blob and convert it to pdf (where i'm blocked)
Here is the part of my code for generate my docx and saving the generated word docx in mongodb (i have intentionnaly delete some things not important for this question)
import React, { useState, useEffect } from "react";
import Docxtemplater from "docxtemplater";
import PizZip from "pizzip";
import PizZipUtils from "pizzip/utils/index.js";
import { saveAs } from "file-saver";
import axios from "axios";
function loadFile(url, callback) {
PizZipUtils.getBinaryContent(url, callback);
}
export const DocxFile = ({ formData }) => {
const [file, setFile] = useState(null);
const [currentlyUploading, setCurrentlyUploading] = useState(false);
const [docxId, setDocxId] = useState(null);
const [progress, setProgress] = useState(null);
const [inputContainsFile, setInputContainsFile] = useState(false);
const [template, setTemplate] = useState();
const generateDocument = () => {
loadFile(template, function (error, content) {
if (error) {
throw error;
}
var zip = new PizZip(content);
var doc = new Docxtemplater(zip, {
paragraphLoop: true,
linebreaks: true,
});
doc.setData({
company: formData.general.companyClient,
version: formData.documentVersion,
quote: formData.general.quoteNumber,
HERE MY DATA //Working :)
});
try {
doc.render();
} catch (error) {
function replaceErrors(key, value) {
if (value instanceof Error) {
return Object.getOwnPropertyNames(value).reduce(function (
error,
key
) {
error[key] = value[key];
return error;
},
{});
}
return value;
}
if (error.properties && error.properties.errors instanceof Array) {
const errorMessages = error.properties.errors
.map(function (error) {
return error.properties.explanation;
})
.join("\n");
}
throw error;
}
var out = doc.getZip().generate({
type: "blob",
mimeType:
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
});
saveAs(
out,
`${name}.${documentVersion}.docx`
);
setFile(out);
setInputContainsFile(true);
});
};
const fileUploadHandler = () => {
const fd = new FormData();
fd.append("docx", file, file.name);
axios
.post(`api/docx/upload`, fd, {
onUploadProgress: (ProgressEvent) => {
setProgress((ProgressEvent.loaded / ProgressEvent.total) * 100);
console.log(
"upload progress",
Math.round((ProgressEvent.loaded / ProgressEvent.total) * 100)
);
},
})
.then(({ data }) => {
setDocxId(data);
setFile(null);
setInputContainsFile(false);
setCurrentlyUploading(false);
})
.catch((err) => {
console.log(err);
if (err.response.status === 400) {
const errMsg = err.response.data;
if (errMsg) {
console.log(errMsg);
}
} else {
console.log("other error", err);
setInputContainsFile(false);
setCurrentlyUploading(false);
}
});
};
const handleClick = () => {
if (inputContainsFile) {
setCurrentlyUploading(true);
fileUploadHandler();
}
};
return (
<>
<button
className="modify__button1 enregistrer generator__button"
onClick={generateDocument}
>
Generate the docx
</button>
<label htmlFor="file" onClick={handleClick}>
{file ? <>SUBMIT</> : <></>}
</label>
</>
);
};
This is what i got on mongodb after sending : MongodbBLOB
After that, i will do a get request to get my blob ( i already know how to get my blob) But then, how can i convert it to pdf (and keep the template used for my docx) ?
Update :
I found a package named docx-pdf which allow me to convert my docx to pdf.
But unfortunately, i've got a lot of webpack problems when i compiled (Can't resolve 'fs', 'child_process', 'path') because "webpack < 5 used to include polyfills for node.js core modules by default."
I will try to resolve this and will update this answer if i succeed to convert my docx :)
I'm using react js version 17, I want to upload file and send it to my node js api.
I have two buttons, The first button used to read the first line in the file,
the second button is for the call to the back end server to save the file in the database as a file
I have create a formData to set my formData file input always i got empty object.
Here's my code :
const [myFile,setMyFile] = useState();
const [headers,setHeaders] = useState();
const readFile = e =>{
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = function(e) {
const text = e.target.result;
const headers = text.slice(0,text.indexOf('\n')).split(';');
setHeaders(headers)
}
reader.readAsText(file);
setMyFile(file)
}
const callApi = e => {
const formData = new FormData();
formData.append(
"file",
myFile,
);
axios.post('localhost:8080/uploadFile', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
My formData always return an empty object {}
Upload logic in react js to send csv in API to backend :
import React, { Component } from "react";
import axios from 'axios';
import AppContext from './AppContext';
const SERVER_URL = process.env.REACT_APP_SERVER_URL;
const PROTOCOL = process.env.REACT_APP_PROTOCOL;
const PORT = process.env.REACT_APP_PORT;
class UploadMeasure extends Component {
constructor() {
super();
this.state = {
csvfile: undefined
};
this.updateData = this.updateData.bind(this);
}
handleChange = event => {
this.setState({
csvfile: event.target.files[0]
});
};
importCSV = () => {
const { csvfile } = this.state;
console.log(csvfile);
var fileName = csvfile.name;
const formData = new FormData();
formData.append(
"fileChooser",
csvfile,
);
axios.post(PROTOCOL + "://" + SERVER_URL + ":" + PORT + "/uploadMeasures/" + AppContext.username + "?&fileName=" + fileName, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(res => { // then print response status
console.log(res)
if (res === 'success') {
alert("File data uploaded Successfully");
} else {
if (res === 'Error') {
alert("Please ensure that your CSV file is formatted using the correct template, if you have any doubt contact the support team.");
} else {
console.log(res)
}
}
})
};
updateData(result) {
var data = result.data;
console.log(data);
}
render() {
console.log(this.state.csvfile);
return (
<div className="App">
<h2>Import CSV File!</h2>
<input
className="csv-input"
type="file"
ref={input => {
this.filesInput = input;
}}
name="file"
placeholder={null}
onChange={this.handleChange}
/>
<p />
<button onClick={this.importCSV}> Upload now!</button>
</div>
);
}
}
export default UploadMeasure;
In react hooks web app, how can we receive a csv file in the server side. The below is not working as I am getting the file undefined in server side. Could someone please advise ?
server.js
const multer = require('multer');
const bodyParser = require("body-parser");
const path = require('path');
app.use(express.static(path.join(__dirname, 'public')));
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/csv')
},
filename: function (req, file, cb) {
var ext = file.originalname.split('.').pop();
cb(null, file.fieldname + '-' + Date.now() + '.' + ext);
}
})
var upload = multer({ storage: storage });
app.put('/service/managenominees', upload.single('file'), async (req, res, next) => {
// csv file has two columns named Name, Email, I would like to receive value from those..
const data = req.file;
try {
if(req.body.file){
var name = req.file.Name;
var email = req.file.Email;
}
var nomineeData = {userName: name, userEmail: email};
res.status(200).send(nomineeData);
} catch (e) {
res.status(500).json({ fail: e.message });
}
});
manageNominee.js
import React, { useRef, useEffect, useState } from "react";
import Axios from "axios";
const ManageNominees = () => {
const [uploadFile, setUploadFile] = React.useState();
const [csvData, setCsvData] = useState([]);
const onChangeCsv = (e) => {
setCsvData(e.target.files[0]);
}
const submitForm = (data) => {
const dataArray = new FormData();
dataArray.append("uploadFile", data);
Axios.put("http://localhost:8000/service/managenominees", dataArray, {
headers: {
"Content-Type": "multipart/form-data"
}
})
.then((response) => {
// successfully uploaded response
})
.catch((error) => {
// error response
});
};
return (
<div>
<form onSubmit={submitForm} encType="multipart/form-data">
<h1>Upload Data</h1>
<input type="file" name="csvfile" onChange={onChangeCsv}/>
<button>Submit</button>
</form>
</div>
)
}
export default ManageNominees
There are two issues:
HTML attribute and the mutler upload option are different.
File values cant be accessed directly either convert the buffer and read the content or read the file (below code reads the file).
const multer = require('multer');
const bodyParser = require("body-parser");
const path = require('path');
const csv = require('csv-parser');
const fs = require('fs');
...
app.put('/service/managenominees', upload.single('csvfile'), (req, res, next) => {
console.log(req.file);
fs.createReadStream(req.file.path)
.pipe(csv())
.on('data', (data) => results.push(data))
.on('end', () => {
console.log(results);
// Result would be array as its CSV, iterate over the array and to get username and email id.
res.status(200).send(results);
});
});
Note: Code does not handle if the file does not exist.
I would like to download a stream file in my project.
Indeed the response form the API is like a blob.
Here i leave my code;
id and filterBody arrives as props to my component.
Really i confused if some where there are mistakes or No?
Can any one help me to find my mistakes?
Thank you.
const fetchBlobFile = async (setBlobFile, id, filterBody) => {
try {
let response = await get(`http://localhost:4000/error/table/export?error_id=${id}`, filterBody)
setBlobFile(response?.data?.blob())
} catch (error) {
}
}
function exportXslFile ({id, filterBody}) {
const [blobFile, setBlobFile] = useState(null)
useEffect(() => {
fetchBlobFile(setBlobFile, id, filterBody)
}, [id])
const export = () => {
const url = window.URL.createObjectURL(blobFile);
window.location = url
}
return (
<button className="export" onClick={export}><ExportXslFile /> Export</button>
)
}
I solved the problem as following.
This code completely works; but only be careful to import necessary packages.
const fetchBlobFile = async (setBlobFileLoading, id, body) => {
try {
const a = document.createElement("a");
a.style.display = "none";
document.body.appendChild(a);
setBlobFileLoading(true);
var data = JSON.stringify(body);
var config = {
method: 'post',
url: `http://localhost:4000/table/export?errorCsv_id=${id}`,
headers: {
'Content-Type': 'application/json'
},
data: data
};
let response = await axios(config);
setBlobFileLoading(false)
const blobFile = new Blob([response?.data], { type: 'text/csv' });
const url = window.URL.createObjectURL(blobFile);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
} catch (error) {
}
}
function exportXslFile ({id, body}) {
const [blobFileLoading, setBlobFileLoading] = useState(false)
const export = () => {
fetchBlobFile(setBlobFileLoading, id, filterBody)
}
return (
{blobFileLoading && <div className="loading fixed"><Loader /></div>} // set up loader
<button className="export" onClick={export}>Export</button>
) //download button
}
I am trying to upload images to an S3 bucket with react and expressjs. When I attempt to upload the image I get a 501 Not Implemented Error. I am using axios to contact the end point I created in the server code.
My react code:
class FileUpload extends Component {
state = {
file: null
};
submitFile = (event) => {
event.preventDefault();
const formData = new FormData();
formData.append('file', this.state.file[0]);
axios.post(`test-upload`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(response => {
// handle your response;
}).catch(error => {
// handle your error
});
}
handleFileUpload = (event) => {
this.setState({file: event.target.files});
}
render () {
return (
<form onSubmit={this.submitFile}>
<input label='upload file' type='file' onChange=
{this.handleFileUpload} />
<button type='submit'>Send</button>
</form>
);
}
}
export default FileUpload;
My server code:
const uploadFile = (buffer, name, type) => {
const params = {
ACL: 'public-read',
Body: buffer,
Bucket: process.env.S3_BUCKET,
ContentType: type.mime,
Key: `${name}.${type.ext}`
};
return s3.upload(params).promise();
};
app.use('/', (req,res) =>{
res.send(JSON.stringify({ greeting: `Hello!` }));
});
// Define POST route
app.post('/test-upload', (request, response) => {
const form = new multiparty.Form();
form.parse(request, async (error, fields, files) => {
if (error) throw new Error(error);
try {
const path = files.file[0].path;
const buffer = fs.readFileSync(path);
const type = fileType(buffer);
const timestamp = Date.now().toString();
const fileName = `bucketFolder/${timestamp}-lg`;
const data = await uploadFile(buffer, fileName, type);
return response.status(200).send(data);
} catch (error) {
return response.status(400).send(error);
}
});
});
This was done by following a guide on the internet but there seems to be something missing that I just can't figure out.
Figured it out in the end,
axios.post(/test-upload,
should have been
axios.post(http://localhost:3000/test-upload,