How to upload images from react native to nodejs? - node.js

I'm trying to upload an image with expo picker image to a nodejs server.
The problem is I never receive the image. I tried so many things I'm desesperate :(
Here is my code :
React-Native
postImage = async (image) => {
const photo = {
uri: image.uri,
type: "image/jpg",
name: "photo.jpg",
};
const form = new FormData();
form.append("test", photo);
axios.post(
url,
{
body: form,
headers: {
'Content-Type': 'image/jpeg',
}
}
)
.then((responseData) => {
console.log("Succes "+ responseData)
})
.catch((error) => {
console.log("ERROR " + error)
});
}
pickImage = async () => {
const result = await ImagePicker.launchImageLibraryAsync({
// mediaTypes: ImagePicker.MediaTypeOptions.All,
// allowsEditing: true,
// aspect: [4, 3],
quality: 1
});
if (!result.cancelled) {
try {
await this.postImage(result);
} catch (e) {
console.log(e);
}
this.setState({ image: result.uri });
}
};
It always works.
And here the nodejs code
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const multer = require('multer');
const fs = require("fs");
const app = express();
const upload = multer({
dest: "upload/",
});
// app.use(upload.single("test"));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(cors());
app.listen(8080, () => {
console.log("running ...")
})
app.post("/upload", upload.single("photo.jpg"), async (req, res) => {
console.log("body =>", req.body);
console.log('files => ', req.files);
console.log("file =>", req.file);
// const oldpath = req.body.;
// const newpath = '/Users/mperrin/test/test-native/test-upload-photo/server/lol.jpg';
// fs.rename(oldpath, newpath, (err) => {
// if (err) {
// throw err;
// }
// res.write('File uploaded and moved!');
// res.sendStatus(200);
// });
res.sendStatus(200);
});
I always see this in the console and I don't know what to do with that ...
body => {
body: { _parts: [ [Array] ] },
headers: { 'Content-Type': 'image/jpeg' }
}
At the moment only the folder "upload" is created.
I don't know where I can get the files, I guess I'm really missing something but I don't know what.
Thanks for help guys !

I've never used multer before but after a quick review of the docs it looks like you need to have the same name in the headers as you're expecting in the node side post
Right now, in your header you have
name: 'photo.jpg'
and the following in your node post
upload.single("test")
Your post is looking for something with the name 'test' not 'photo.jpg' and you're sending 'photo.jpg'
Try it out and let me know how it goes.
Edit: My mistake, you may have add
name: "test"
to the headers here instead of in the photo object:
axios.post(
url,
{
body: form,
headers: {
'Content-Type': 'image/jpeg',
}
})

Related

getting this error: Error: Unexpected end of form

When trying to submit an audio file to backend from the front end im getting two errors error: Error: Unexpected end of form
error: uploading file: Error: Failed to upload audio file
im using form data to send an audio file on the front end and multer and gridfs on the backend. does anybody know why i am getting this error.
When trying to submit an audio file to backend from the front end im getting this error: Error: Unexpected end of form, im using form data to send an audio file on the front end and multer and gridfs on the backend. does anybody know why i am getting this error.
here is my app.js
import React, { useState } from 'react';
const App = () => {
const [selectedFile, setSelectedFile] = useState(null);
const handleFileChange = (event) => {
setSelectedFile(event.target.files[0]);
}
const handleFormSubmit = (event) => {
event.preventDefault();
const formData = new FormData();
formData.append('audioFile', selectedFile);
fetch('http://localhost:4002/audio', {
method: 'POST',
body: formData,
})
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Failed to upload audio file');
}
})
.then(data => {
console.log('File uploaded successfully:', data);
// Do something with the response data
})
.catch(error => {
console.error('Error uploading file:', error);
});
}
return (
<div className="flex h-[100vh] w-[100%] items-center justify-center">
<form onSubmit={handleFormSubmit} encType="multipart/form-data">
<input type="file" onChange={handleFileChange} />
<button type="submit" disabled={!selectedFile}>Upload</button>
</form>
</div>
);
};
export default App;
here is my sever.js
const express = require('express');
const mongoose = require('mongoose');
const multer = require('multer');
const { GridFsStorage } = require('multer-gridfs-storage');
const Grid = require('gridfs-stream');
const cors = require('cors');
const path = require('path')
const bodyParser = require("body-parser")
const app = express();
app.use(express.json());
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
mongoose.connect('mongodb+srv://jordandeeds31:Jd400089#cluster0.ibeioeb.mongodb.net/?retryWrites=true&w=majority', {
useUnifiedTopology: true,
useNewUrlParser: true,
}).then(() => {
console.log('Connected to MongoDB');
}).catch((err) => {
console.error(err);
});
const conn = mongoose.connection;
let gfs;
conn.once('open', () => {
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('audioFiles');
});
const storage = new GridFsStorage({
url: 'mongodb+srv://jordandeeds31:Jd400089#cluster0.ibeioeb.mongodb.net/grid?retryWrites=true&w=majority',
file: (req, file) => {
return {
filename: file.originalname,
bucketName: 'audioFiles'
};
}
});
const upload = multer({
storage: storage,
limits: {
fileSize: 90 * 1024 * 1024 // 10MB
},
fileFilter: (req, file, cb) => {
if (file.mimetype.startsWith('audio/')) {
cb(null, true);
} else {
cb(new Error('File type not supported.'));
}
}
});
// Add this middleware before the POST route
app.use(upload.any());
const audioFileSchema = new mongoose.Schema({
fileUrl: {
type: String,
required: true
},
audioData: {
type: Buffer,
required: true
}
});
const AudioFile = mongoose.model('AudioFile', audioFileSchema);
app.post('/audio', upload.single('audioFile'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ message: 'No file uploaded.' });
}
const audioFile = new AudioFile({
fileUrl: `http://localhost:4002/audio/${req.file.filename}`,
audioData: req.file.buffer
});
console.log('req.file.buffer:', req.file.buffer);
const savedAudioFile = await audioFile.save();
res.json({ fileUrl: savedAudioFile.fileUrl });
} catch (err) {
console.error(err);
if (err instanceof multer.MulterError) {
res.status(400).json({ message: 'File upload error.' });
} else if (err.message === 'File type not supported.') {
res.status(400).json({ message: err.message });
} else {
res.status(500).send('Internal Server Error');
}
}
});
app.get('/audio/:filename', (req, res) => {
const { filename } = req.params;
const readStream = gfs.createReadStream({ filename });
readStream.on('error', (err) => {
console.error(err);
res.status(404).send('File not found.');
});
readStream.pipe(res);
});
app.listen(4002, () => {
console.log('Listening on port 4002');
});

Why is req.file undefined in my route.post?

I am trying to send a photo from react to node server.
But i have some problems : req.file is undefined and I don't understand why
This is my react code
const formData: any = new FormData();
formData.append("file", {
uri: image.uri,
type: image.type,
name: "tmpImage",
});
try {
await axios.post(`${BACKEND_URL}/recipes/photo`, {
formData,
header: { "Content-Type": "multipart/form-data" },
});
} catch (error) {
console.error("Error when sending image");
}
and here is my nodejs code
multer conf :
export const storage = multer.diskStorage({
destination: function (req, file, callback) {
console.log("here dest");
callback(null, "uploads");
},
filename: function (req, file, callback) {
console.log("here filename");
callback(null, file.originalname);
},
});
export const upload: any = multer({ storage });
route :
recipesRoutes.post(
"/photo",
upload.single("tmpImage"),
async (req: Request, res: Response) => {
console.log("req files =>", req.files);
console.log("req file =>", req.file);
return res.status(200).send();
}
);
express conf :
app.use(cors());
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
req.file is undefined and I don't understand why.
Thanks for help
I think you need to use the different curly brackets for the data that you are sending
and the headers that you configuring. it must be something like this:
await axios.post(`${BACKEND_URL}/recipes/photo`,
{formData},
{header: { "Content-Type": "multipart/form-data" }},
);
based on this example :
import axios from 'axios';
axios.post('https://httpbin.org/post', {x: 1}, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(({data}) => console.log(data));
on this page
GitHub

uploading image to nodejs backend (express multer ) from expo app

I have a backend and a create ticket route, in my web react app the image upload works fine but in react native it was not working
here is my react native pickimage code
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.cancelled) {
setImage(result.uri);
}
};
it was only set the file path to setImage and after it wasn't get uploaded
the request code
here I make the request but everything gets saved on the database except the image
const sendticket = async (title, description, image) => {
setIsLoading(true);
if (title === "" || description === "") {
Alert.alert("Error", "Please fill all the fields");
return;
}
const url = `${BASE_URL}/ticket/`;
try {
const formData = new FormData();
formData.append("title", title);
formData.append("description", description);
formData.append("image", image);
formData.append("creator", userInfo.userId);
const response = await fetch(url, {
method: "POST",
body: formData,
headers: {
Authorization: "Bearer " + userInfo.token,
},
});
const data = await response.json();
if (response.status === 201) {
Alert.alert("Success", "Ticket " + data.Ticket.number + " created");
} else {
console.log(response.status);
console.log(response.statusText);
Alert.alert("Error", "Something went wrong");
}
} catch (error) {
console.log(error);
}
setIsLoading(false);
};
there is the route and multer file upload BACKEND
route.post
router.post(
"/",
wenauppload.single("image"),
[check("title").not().isEmpty(), check("description").isLength({ min: 5 })],
ticketcontroller.createTicket
);
file-upload.js
const multer = require("multer");
const uuid = require("uuid");
const MIME_TYPE_MAP = {
"image/png": "png",
"image/jpeg": "jpeg",
"image/jpg": "jpg",
};
const fileUpload = multer({
limits: 1500000,
storage: multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/images");
},
filename: (req, file, cb) => {
const ext = MIME_TYPE_MAP[file.mimetype];
cb(null, uuid.v4() + "." + ext);
},
}),
fileFilter: (req, file, cb) => {
const isValid = !!MIME_TYPE_MAP[file.mimetype];
let error = isValid ? null : new Error("Invalid mime type!");
cb(error, isValid);
},
});
module.exports = fileUpload;

Multer with node.js / react-native not working

I'm creating an application with reaction-native, and I'm using an image picker to select an image and click the button to create a part that sends the image to the node.js server.
However, in the process of uploading an image, other additional information is normally stored in mysql, but images are not stored in the upload folder.
Multer version 1.4.2.
Node.js version 16.12.0
in react-native code ( data to node.js)
onPress={() => {
if (this.state.image === null) {
alert("이미지를 넣어주세요");
} else {
Alert.alert(
"구인 공고를 등록할까요?",
"등록후, 수정할 수 없으니 꼼꼼히 확인 부탁~!!",
[
{
text: "Cancel",
onPress: () => alert("취소하였습니다."),
style: "cancel"
},
{
text: "OK",
onPress: async () => {
const formData = new FormData();
formData.append("db_title", this.state.title);
formData.append("db_wtype", this.state.type);
formData.append("db_sdate", this.state.start);
formData.append("db_edate", this.state.end);
formData.append("db_money", this.state.money);
formData.append(
"db_address",
this.state.address
);
formData.append(
"db_description",
this.state.addition
);
formData.append("file", this.state.image);
formData.append("db_stime", "9");
formData.append("db_etime", "18");
formData.append("db_smin", "00");
formData.append("db_emin", "30");
await AsyncStorage.getItem("pubKey").then(
(pubKey) => {
formData.append("db_pubkey", pubKey);
}
);
const {
data: { result }
} = await axios.post(
"http://127.0.0.1:4000/upload",
formData
);
console.log(result);
alert(result);
this.props.navigation.navigate("Announce");
}
}
],
{ cancelable: false }
);
}
}}
in my node server code
const multer = require('multer');
const _storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/upload')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
const upload = multer({ storage: _storage });
router.post("/", upload.single("file"), function (req, res) {
console.log(req.body);
console.log(req.body.file);
Article.create({
db_title: req.body.db_title,
db_wtype: req.body.db_wtype,
db_sdate: req.body.db_sdate,
db_edate: req.body.db_edate,
db_stime: req.body.db_stime,
db_etime: req.body.db_etime,
db_smin: req.body.db_smin,
db_emin: req.body.db_emin,
db_pubkey: req.body.db_pubkey,
db_money: req.body.db_money,
db_address: req.body.db_address,
db_description: req.body.db_description,
db_img: req.body.file
})
.then(result => {
console.log("result : " + result);
res.status(201).json({ result: "공고가 등록 되었습니다." });
})
.catch(err => {
console.error("err : " + err);
});
});
module.exports = router;
this is node.js console log
edit image picker (this.state.image)
_pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3]
});
console.log("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ");
console.log(
result
);
console.log("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ");
if (!result.cancelled) {
this.setState({ image: result.uri });
}
};
You need to use the path made by multer:
change:
db_img: req.body.file
to
db_img: req.file.path

Unable to upload image/video files from react-native to nodejs

I am using expo-image-picker in react-native to pick image/video files and multer in nodejs as middleware to download files in directory /public/upload. When i am uploading file along with other parameters from react-native, multer is unable to detect a file present in req.body and hence not downloading any file.
Here is my react-native code using axios
pickImage = async () => {
try {
let options = {
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 1,
// base64:true
}
let result = await ImagePicker.launchImageLibraryAsync(options)
if (!result.cancelled) {
this.setState({ content: result })
}
} catch (E) {
console.log("error in picking image:", E)
}
}
createFormData = (response) => {
const photo = {
uri: response.uri,
type: response.type,
name: "my-img.jpg",
};
const form = new FormData();
form.append('acivityFile',photo);
return form;
};
handleSubmit = async () => {
if (this.state.content) {
const formData = this.createFormData(this.state.content)
console.log("form data:", formData)
try {
const res = await axios.post('http://393ad751391b.ngrok.io/activities',
{
title: "This is the title",
description: "This is a description",
eventType: "LOST & FOUND",
file: formData
},
{
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data'
},
},
)
console.log("res:", res.data);
} catch (err) {
console.log("err in post axios:",err)
}
}
}
Here is my route file handling http requests in server-side
const express = require('express');
const Upload = require('./../utils/multerSetup');
const activityController = require('../Controllers/activityController');
const router = express.Router();
router
.route('/')
.get(activityController.getAllActivities)
.post(
Upload.single('activityFile'),
activityController.addActivity
);
Here is my multerSetup.js file in server-side
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/uploads/');
},
filename: function (req, file, cb) {
const ext = file.mimetype.split('/')[1];
cb(null, file.fieldname + '-' + Date.now() + '.' + ext);
},
});
const upload = multer({ storage });
module.exports = upload;
Here is my activityController.js file in server-side
const Activity = require('./../modals/activityModel');
const User = require('./../modals/user');
exports.getActivity = async (req, res, next) => {
console.log('here');
const activity = await Activity.findById(req.params.id);
res.status(200).json({
status: 'success',
data: {
activity,
},
});
};
exports.addActivity = async (req, res, next) => {
if (req.file) {
let file = {
Ftype: req.file.mimetype.split('/')[0],
name: req.file.filename,
};
req.body.file = file;
}
if (!req.body.location) {
req.body.location = {
coordinates: ['77.206612', '28.524578'],
};
}
if (req.body.votes) {
req.body.votes.diff = req.body.votes.up - req.body.votes.down;
}
req.body.creator = "12345" || "req.userId";
const activity = await Activity.create(req.body);
res.status(201).json({
status: 'success',
data: {
activity,
},
});
};
Also when Content-type:'multipart/form-data, then server console throws Error: Multipart:Boundary not found. When i use Content-type:'application/json', then multer does not work.
I just want to know what is the correct way of uploading files with additional parameters from react-native to nodejs multer. Any suggestions would be a great help!

Resources