Pass Files to Node Server - reactjs

Im having this function in my react app, what is does is sending a file to the server and the server save it to the declared dir.
const handleUploadfile = (event) => {
event.preventDefault();
const data = new FormData();
data.append('file',file );
fetch("http://localhost:4000/upload", {
method: 'POST',
body: data
}).then((response) => console.log(response))
}
heres the code in the node server handling the request above
app.post("/upload", (req, res) => {
const newpath = "./files/";
const file = req.files.file;
const filename = file.name;
file.mv(`${newpath}${filename}`, (err) => {
if (err) {
res.status(500).send({ message: "File upload failed", code: 200 });
}
res.status(200).send({ message: "File Uploaded", code: 200 });
});
});
The upload is working well, but what i want to know is, am i able to include some data like a string directory for each uploaded file something like that.
BTW im using express-uploadfile middleware so i can access the files.

If you want to upload another filed in your request you simply need to call append on your FormData object.
const data = new FormData();
data.append('file',file );
data.append('string','some string');
Then read it on the server
const string = req.body.string;

Related

Sending formdata from React to express using multer

This is a MERN app that has a form consisting of text inputs and a file input. The problem: I am unable to access the file input in my backend.
Start of controller function:
async function create(req, res) {
function base64_encode(file) {
let bitmap = fs.readFileSync(file)
return bitmap.toString('base64')
}
console.log(req.file)
let image = base64_encode(req.file.path)
The console.log of req.file returns "undefined" and I get error message "Cannot read properties of undefined (reading 'path')" referring to req.file.path.
The route:
const multer = require("multer")
const upload = multer({ dest: '../../uploads/'})
router.post("/", upload.single('image'), spotCtrl.create)
It should be noted that the image file is not showing up in the image folder I created.
The handleSubmit function:
handleSubmit = async () => {
let chosenImage = this.state.image
const formdata = new FormData()
formdata.append('image', chosenImage)
let body = {
name: this.state.name,
description: this.state.description,
address: this.state.address
}
let options = {
method: 'POST',
body: JSON.stringify(body),
formdata: formdata
}
await fetch("/api", options)
.then(res => res.json())
}
The <form tag has encType="multipart/form" set as well.
I've installed multer, fs, express-fileupload, among others
A couple maybe relevant lines from my server.js:
app.use(express.json(), expressfileupload());
app.use(express.static(path.join(__dirname, 'build')));
I have tried in my controller function to use req.files instead of req.file however I get the same result.

Upload file with React

I want to make a simple file upload form on the front end. Then, on the backend, I would pass the information about that file to an API.
Here is my front-end code where I call a specific function on the back end and pass the data:
import React from 'react';
import Axios from 'axios';
const Upload = () => {
// a local state to store the currently selected file.
const [selectedFile, setSelectedFile] = React.useState(null);
const handleSubmit = async (event) => {
event.preventDefault()
//Got all the Infos about my file
console.log(selectedFile)
const formData = new FormData();
formData.append("selectedFile", selectedFile);
//Empty result
console.log(formData)
Axios.get("http://localhost:3001/upload", {
//I will pass the data to a function in the backend
params: {
data: formData,
},
})
.then((Response) => {
console.log(Response)
})
.catch(function (error) {
console.log(error);
});
}
const handleFileSelect = (event) => {
setSelectedFile(event.target.files[0])
}
return (
<form onSubmit={handleSubmit}>
<input type="file" onChange={handleFileSelect}/>
<input type="submit" value="Upload File" />
</form>
)
};
export default Test
On the back-end side, a route call the method
router.get('/upload?', Upload);
Then finally the function in the backend to process
const ApiProcess = (req, res) => {
var axios = require('axios');
var data = req.query
console.log(req.query)
//All the API Stuff
}
But the problem is that I receive empty data in the Backend. What's wrong with my code?
Thanks
EDIT
On backend side I use multer and add 'app.use(multer().any())' on top of index file. That help cause now I cant access in backend to a simple formData. Now my function that receive the data log this '[Object: null prototype] {}'
Any idea ?
This is because your file is not getting forwarded from frontend
use FileReader instead
<input type="submit" value="Upload File" onChange={(e) =>
setFile(e.target.files)} />
const data = new FormData();
data.append(file[0])
and then you can access the file data on file[0] index and after storing the data you can forward it to the backend
there are some problems in your code.
first of all an upload request usually is a post type. and also you should send Content-Type header with your request. so:
Axios.post("http://localhost:3001/upload", formData {
headers: {
'Content-Type': 'Multipart/formData',
},
})
when you log formData it's always empty. you can use some methods like formData.keys() or formData.values() to see inside it.
Ok I got the solution. I missed a piece of middleware to process Multipart/formdata on Express Side :
const router = express.Router();
const multer = require("multer");
//Set the destination folder and Naming of the file that is upload
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, file.originalname)
}
})
const upload = multer({ storage: storage })
Then I process the formData with the files
router.post('/upload', upload.array("file"),Upload);
Thanks a lot for your help

sending more data along with formData to an API with axios

I´m writing an API that loads files in a folder
In order to select the files I’m using FormData. I use
<input type='file' ref={inputElement} onChange={handleChange} />
After choosing the file, In use axios to make a request
const uploadFile = () => {
const formData = new FormData();
formData.append('file', file); // appending file
axios
.post('http://localhost:3001/upload', formData, {…
The api receives it and does it’s thing
app.post('/upload', (req, res) => {
if (!req.files) {
return res.status(500).send({ msg: 'file not specified' });
}
// accessing the file
const myFile = req.files.file;
It works fine.
But, I’d like to send extra info to the endpoint, so, I send the extra info and the formdata to axios:
const uploadFile = () => {
const formData = new FormData();
formData.append('file', file); // appending file
axios
.post('http://localhost:3001/upload', {data: formData, extraInfo: 'more info'}, {
And in the endpoint I write:
app.post('/upload', (req, res) => {
console.log(req.body.extraInfo)
console.log(req.body.data)
extraInfo 'more info', ok, but data is empty, I supposed that data should contain formdata, but it’s empty, what can I do in order to get the formData and the extraInfo at the same time
Thanks in advance
Rafael
Just add the extraInfo to the formData and then send it to your server. You may need to double check how your server wants to get the data.
const uploadFile = () => {
const formData = new FormData();
formData.append('file', file); // appending file
formData.append('extraInfo', "Some Info");// additonal data
axios
.post('http://localhost:3001/upload', formData, {

FormData with NextJS API

Background
I am trying to create a simple CRUD application using NextJS along with react-redux, so what it does is that it saves peoples contacts.So when adding a contact i am trying to send some data along with a file to a NextJS API.
Issue
ContactAction.js
Make a POST request from redux action to add a contact
export const addContact = (data) => async (dispatch) => {
try {
var formData=new FormData();
formData.append('name',data.Name);
formData.append('email',data.Email);
formData.append('phone',data.Phone);
formData.append('image',data.Image);
let response= await Axios.post(`http://localhost:3000/api/contact/addContact`,formData,{
headers:{
'x-auth-token':localStorage.getItem('token')
}
});
} catch (error) {
console.log(error);
}
}
addContact.js
This is the API route in /api/contact/
const handler = async (req, res) => {
switch(req.method){
case "POST":{
await addContact(req,res)
}
}
}
const addContact = async (req, res) => {
console.log(req.body);
// do some stuff here and send response
}
this is what i get in the terminal after the log,also the file is Gibberish as well when logging req.files
Current Effort
I tried using third party packages such as formidable and formidable-serverless but got no luck. so after a day i made it work with a package called multiparty.
addContact.js
const handler = async (req, res) => {
switch(req.method){
case "POST":{
let form = new multiparty.Form();
let FormResp= await new Promise((resolve,reject)=>{
form.parse(req,(err,fields,files)=>{
if(err) reject(err)
resolve({fields,files})
});
});
const {fields,files} = FormResp;
req.body=fields;
req.files=files;
await addContact(req,res)
}
}
}
const addContact = async (req, res) => {
console.log(req.body); //Now i get an Object which i can use
// do some stuff here and send response
}
The above solution is obviously redundant and probably not the best way to go about it plus i don't want to add these 7 8 lines into each route.
so if someone could help me understand what i am doing wrong and why formData doesn't seem to work with NextJS API (when it works with the Express server) i would be grateful.
FormData uses multipart/form-data format. That is not a simple POST request with a body. It is generally used for uploading files, that's why it needs special handling. As an alternative, you could use JSON.
Here is my solution, i hope this helps anybody.
First of all you need to install next-connect and multer as your dependencies.
Now you can use this API route code.
import nextConnect from "next-connect";
import multer from "multer";
const apiRoute = nextConnect({
onError(error, req, res) {
res.status(501).json({ error: `Sorry something Happened! ${error.message}` });
},
onNoMatch(req, res) {
res.status(405).json({ error: `Method "${req.method}" Not Allowed` });
},
});
apiRoute.use(multer().any());
apiRoute.post((req, res) => {
console.log(req.files); // Your files here
console.log(req.body); // Your form data here
// Any logic with your data here
res.status(200).json({ data: "success" });
});
export default apiRoute;
export const config = {
api: {
bodyParser: false, // Disallow body parsing, consume as stream
},
};
Here is an example about uploading file with Next.js:
https://codesandbox.io/s/thyb0?file=/pages/api/file.js
The most important code is in pages/api/file.js
import formidable from "formidable";
import fs from "fs";
export const config = {
api: {
bodyParser: false
}
};
const post = async (req, res) => {
const form = new formidable.IncomingForm();
form.parse(req, async function (err, fields, files) {
await saveFile(files.file);
return res.status(201).send("");
});
};
const saveFile = async (file) => {
const data = fs.readFileSync(file.path);
fs.writeFileSync(`./public/${file.name}`, data);
await fs.unlinkSync(file.path);
return;
};
Generally speaking,in your api file,you should disable the default bodyParser,and write your own parser

File Upload and download JavaScript

EDIT
Hope someone can clarify this issue I am having:
I want to store files in the backend inBinary format, and after get them and convert them into the original state of the file, I believe there is something wrong with my code, I am using HTML upload
this is how I send the binary data to backend:
const handleSubmit = async file => {
let formData = new FormData()
formData.append('file', file)
try {
const res = await postInvoice(uuid, formData)
console.log(res)
setLoadingInvoice(false)
setState({
fileList: [],
attachments: [],
filesConverted: [],
})
}
}
**edit with the api call, only missing hte url part which is in a different file**
export const postInvoice = async (id, data) => {
return await request
.post(BRattachments().post(id))
.send(data)
.set(getHeaders())
}
this is how I try to download te bianrry data:
const handleDownload = async value => {
try {
const res = await getInvoice(value.id)//api call
const file = new Blob([response], {
type: res.type,
})
saveAs(file, value.id + '.' + res.extension)//using save as to save import saveAs from 'file-saver'
}
}
**edited,this is the API CALL,I have them groued in a different file**
export const getInvoice = async ref => {
return await request
.get(BRattachments().getAll(ref))
.send()
.set(getHeaders())
.responseType('blob')
}
the problem is that every time I try to download any saved file, it gives me a problem, with XLS I only see null in the file, with PNG it says file not PNG, etc.
Hope someone can clarify how can I download binary file and convert it in a downloadable file.
thanks

Resources