React Native IOS application can not upload image through Express+ multer server - express

React Native IOS application, want to upload image; from device.
RN 0.39.2
Client:
const formData = new FormData()
formData.append('files', file)
formData.append('type', 'image')
fetch(API_HOST+UPLOAD_AVATAR,{
method:'post',
headers: {'Content-Type':'multipart/form-data;boundary=6ff46e0b6b5148d984f148b6542e5a5d','Authorization': 'Bearer'+' '+token},
body: formData
})
.then(response=>response.json())
.then(data=>{
//console.log(data)
//Alert.alert(data)
})
.catch(error=>{
console.log(error)
})
Server :
var multer = require('multer');
var upload = multer();
router.post('/user', ensureAuthenticated, upload.any(), function (req, res) {
console.log(req.body);
console.log(req.files);
})
Error:
server req.body and req.files are empty.
Then I try to use RNFetchBlob.
RNFetchBlob.fetch('POST', API_HOST+UPLOAD_AVATAR, {
'Content-Type':'multipart/form-data;boundary=6ff46e0b6b5148d984f148b6542e5a5d'
'Authorization' : 'Bearer'+' '+token
}, formData)
.then((resp) => {
}).catch((err) => {
// ...
})
then error change to
NSMutableDictionary cannot be converted to NSString.
And req.body is {}, req.files is undefined

I assume you found a solution to this, if yes, could you share it?.
In any case, for the RNFetchBlob issue, I used to get the same error and I solved by changing FormData to an array. Like this:
const body = [{
name: 'data',
data: JSON.stringify(whateverData)
}, {
name: 'file',
data: filePath,
}];
…
RNFetchBlob.fetch('POST', apiEndpoint, headers, body);
Hope that helps.

Related

React Native Image Upload with FormData to NestJS server using a FileInterceptor, but 'file' is undefined

My React Native application receives a selected image using the react-native-image-picker library and I need to send that image to a back-end running a NestJS server. The endpoint uses #UseInterceptor and FileInterceptor to extract the image from the 'file' field of the formData received. However, when I fire the request to the endpoint, the file received is undefined.
Here is my React Native code sending the request with the file in the FormData as a payload.
const uploadNewProfileImage = async () => {
if (!newProfileImage?.assets) return;
const formData = new FormData();
const profileImage = newProfileImage.assets[0];
console.log(profileImage);
if (profileImage.uri && user) {
formData.append(
'file',
JSON.stringify({
uri:
Platform.OS === 'android'
? profileImage.uri
: profileImage.uri.replace('file://', ''),
name: profileImage.fileName,
type: profileImage.type
})
);
client // client is an Axios instance that injects Bearer Token
.post(`/user/profile/${user.uid}/image`, formData)
.then(({ data }) => {
console.log(data);
})
.catch((err) => {
console.log(err.response);
setShowImageUploadError(true);
})
.finally(() => {
getUserProfile();
});
}
};
Here is my back-end NestJS code extracting the file.
// User.controller.ts
#UseGuards(UserGuard)
#ApiBearerAuth()
#ApiUnauthorizedResponse({ description: 'Unauthorized' })
#UseInterceptors(FileInterceptor('file', { limits: { fileSize: 20000000 } }))
#Post('/profile/:uid/image')
#ApiOkResponse({ type: UploadProfileResponse })
#ApiBadRequestResponse({ description: 'Image too large OR Invalid image type' })
async uploadProfilePicture(#UploadedFile() file: Express.Multer.File, #Request() req): Promise<UploadProfileResponse> {
const uid = req.user.uid;
const imageUrl = await this.userService.uploadProfilePicture(uid, file);
return imageUrl;
}
}
I tried to set the axios request header in the axios config like so
{
headers: {
'Content-Type': 'multipart/form-data; boundary=——file'
}
}
I tried chaning the back-end endpoint to the following
#UseGuards(UserGuard)
#ApiBearerAuth()
#ApiUnauthorizedResponse({ description: 'Unauthorized' })
#UseInterceptors(FileFieldsInterceptor([{ name: 'file' }], { limits: { fileSize: 20000000 } }))
#Post('/profile/:uid/image')
#ApiOkResponse({ type: UploadProfileResponse })
#ApiBadRequestResponse({ description: 'Image too large OR Invalid image type' })
async uploadProfilePicture(#UploadedFiles() file: Array<Express.Multer.File>, #Request() req): Promise<UploadProfileResponse> {
const uid = req.user.uid;
console.log("File", file);
const imageUrl = await this.userService.uploadProfilePicture(uid, file[0]);
return imageUrl;
}
Nothing seems to be working, and the file extracted from the backend is still undefined.
Any help would be greatly appreciated.

Axios + Multer Express req.file undefined

I'm trying to simply upload a single file from the client (react/axios) to the server (multer / express). I've read through every "req.file undefined" and can't seem to see the same issues with my own code.
The other issue is that actually my req on the server sees the file in the "files", but multer doesn't save it and req.file is undefined.
What could be happening here?
For client I've tried both methods of sending the form data, neither work.
const onAnalyze = async () => {
if (selectedFile !== null) {
//we have a file, so that's what we're sending
var formData = new FormData();
formData.append("analyze", selectedFile);
//let res = await api.post('/analyze/upload', formData)
try {
const response = await axios({
method: "post",
url: "http://localhost:5000/analyze/upload",
data: formData,
header: { "Content-Type": "multipart/form-data" }
});
console.log(response)
} catch (error) {
console.log(error)
}
// console.log(res)
// setAnalysis(res.data)
} else if (text.length <= maxLength) {
let res = await api.post('/analyze', { text: text })
setAnalysis(res.data)
}
}
For the server it seems simple.. I just don't know. This file destination exists. req.file is always undefined
import express from 'express';
import { getMedia, createMedia } from '../controllers/media.js';
import { AnalyzeText, AnalyzeFile } from '../controllers/analyze.js'
import multer from 'multer'
const fileStorageEngine = multer.diskStorage({
destination: "uploads",
filename: (req, file, cb) => {
cb(null, file.originalname)
}
});
var upload = multer({ storage: fileStorageEngine })
const router = express.Router();
//Get All Movies and TV shows.
router.get('/', getMedia);
//Request to create a new item based on a title
router.post('/', createMedia);
//Recuist to analyze information (not sure if this should be a post or not)
router.post('/analyze', AnalyzeText)
router.post('/analyze/upload', upload.single('analyze'), (req, res) => {
console.log(req.file)
res.status(200).json('well we found it again');
});
Turns out I had another middleware running that was wrapping my file upload. Removed that, everything works.
If you're using react you may face this problem sending your request with axios. But I solved it by adding a name attribute to my input element. And removing the new formData method totally and passing the input.file[0] into axios, content-type multipart-formdata, and you must use the multer.diskStorage method. If not your image would be saved as text file

Cloudinary\Error: Missing required parameter - file - Express and Postman

first time trying to upload images to Cloudinary and I have come across an issue when using Express via Postman.
Using form-data on setting 'file' to upload an image to Cloudinary
As of now, when I try to access the req.body I get an empty object, so I guess that has to do with why cloudinary.uploader.upload cannot read the file passed as its first param, since its req.body.file, as shown in the code below.
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_KEY,
api_secret: process.env.CLOUDINARY_SECRET
})
exports.upload = async (req, res) => {
try{
console.log(req.body);
const result = await cloudinary.uploader.upload(req.body.file, {
public_id: `${Date.now()}`,
resource_type: "auto"
})
return res.json({
public_id: result.public_id,
url: result.secure_url
})
}catch(err){
console.log(err)
}
}
The error message I get:
{
message: 'Missing required parameter - file',
name: 'Error',
http_code: 400
}
Any suggestions to solve this issue?
I solved it! I was not able to pass the form-data as req.body to the server, so I had to try and access it through req.files, but was not able to with that either, so I searched a bit and found a middleware 'express-fileupload', and that did the trick. I just added it in my app.js and used
const fileupload = require('express-fileupload');
app.use(fileupload({useTempFiles: true}))
So now I can access my req.files.
exports.upload = async (req, res) => {
const file = req.files.image
try{
console.log(file);
const result = await cloudinary.uploader.upload(file.tempFilePath, {
public_id: `${Date.now()}`,
resource_type: "auto"
})
res.json({
public_id: result.public_id,
url: result.secure_url
})
}catch(err){
console.log("Error", err)
return res.status(400).json({error: err})
}
}
The response I get is:
{
name: 'some-image.png',
data: <Buffer >,
size: 99770,
encoding: '7bit',
tempFilePath: ' **C:\\filepath\some-image.png** ',
truncated: false,
mimetype: 'image/png',
md5: 'b5f612a571442bf604952fd12c47c1bf',
mv: [Function: mv]
}
POST /cloudinary/upload-images 200 1617.944 ms - 119
And it is uploaded successfully to my Cloudinary.
const result = await cloudinary.uploader.upload(req.file, {
public_id: ${Date.now()},
resource_type: "auto"
})
and add file from form data and type should be File
Solved!
This is how i am setting the FormData
let myTestForm = new FormData();
myTestForm.set("name", name);
myTestForm.set("email", email);
myTestForm.set("Avatar", Avatar);
myTestForm.set("password", password);
This is how i am using the FormData
const config = {
headers: {
"Content-type": "multipart/form-data",
},
};
const { data } = await axios.post(`/api/v1/register`, userData, { config });
please don't pass it this way { userData} , had struggled for with this :/
This is how i am uploading image
const myCloud = await cloudinary.v2.uploader.upload(req.body.Avatar, {
folder: "Avatars",
width: 150,
crop: "scale",
public_id: `${Date.now()}`,
resource_type: "auto",
});
PS : in my case i had to upload only 1 image. Have not passed any parameter in app.js file
app.use(bodyParser.urlencoded({ extended: true }));
app.use(fileUpload());

Network Error when sending file in react native with axios

I am trying to send a file to a nodejs server from react native using axios, this is my code:
const createFormData = (file) => {
const data = new FormData();
data.append('message', text);
data.append('receiver',doctorid);
if(file !== ''){
data.append('file', {
type: file.type,
uri: file.uri,
name: file.name.replace(/\s/g,'')
})
}
return data;
}
const onSend = async() => {
const newMessages = [...messages]
newMessages.push({"sender": currentuserID, "id": 339, "message": 'sending...', "attachment": '', "receiver": doctorid, "type": 0},)
setMessages(newMessages)
const token = await AsyncStorage.getItem('token');
const data = createFormData(singleFile)
await appApi.post('/chats', data, {
headers: { 'Authorization': 'Bearer ' + token }
}).then(()=>{
socket.emit('sendmessage', text, (err) => {
messageInit()
});
})
.catch(err => console.log(err.message))
}
This code works perfectly if there's no image attached, but ones there's an image attached, I get the network error message immediately.
For a little bit of troubleshooting, I tried sending request to my local machine, using ngrok. From ngrok, I realized the request wasn't sent at all to the url. So it just fails immediately, without the request been made to the url.
Anyone with solution to this.
I'm testing on an android emulator
send using formdata
try this
let formData = new FormData();
let imagefile = document.querySelector('#file');
formData.append("image", imagefile.files[0]);
axios.post('upload_file', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})

React Native how to upload multiple photos to server at once

I've tried to upload multiple images to server at a once by using fetch.
Here is my code chip.
chooseImage(){
ImagePicker.openPicker({
multiple: true,
waitAnimationEnd: false
}).then(images => {
var photos = []
images.map((image, i) => {
photos.push({
uri:image.path,
name: image.name,
type: 'image/jpg'
})
let source = {uri: image.path}
this.state.photos.push({image: source, check: true, hairstyle: '', price: ''})
})
var data = new FormData();
data.append('photos', photos)
const config = {
method: 'POST',
headers: {
'Accept':'application/json',
'Content-Type':'multipart/form-data;',
'Authorization': this.props.auth.token
},
body: data,
}
fetch("http://**.***.***.***/api/providers/uploadPhotos", config)
.then(res=>res.json()).then((res) => {
console.log("----------Response----------")
console.log(res)
this._getStylist()
this.setState({photo_take: false});
}).catch(err=>{
console.log("------------Error-----------")
console.log(err)
console.log("error in uploading image")
}).done()
}).catch(e => {
console.log(e);
});
}
When I try it, I get 200 response from server and photos doesn't upload to server actually.
I searched the solution a few days, but couldn't find the suitable solution.
Thanks for any suggestion.
I am new at react native but seems like you can loop throght images array to upload images Maybe you can add this post actions in promise array then you can be sure that every photo added to your storage. I use promise array methods while working on my node js server project its very useful.
Edit for example:
var photos=[];
var promise_array=[];
photos.forEach(function(photo){
const config = {
method: 'POST',
headers: {
'Accept':'application/json',
'Content-Type':'multipart/form-data;',
'Authorization': this.props.auth.token
},
body: photo,
}
promise_array.push(fetch("http://**.***.***.***/api/providers/uploadPhotos", config)
.then(res=>res.json()).then((res) => {
console.log("----------Response----------")
console.log(res)
this._getStylist()
this.setState({photo_take: false});
}).catch(err=>{
console.log("------------Error-----------")
console.log(err)
console.log("error in uploading image")
}).done())
})
Promise.all(promise_array);
Since fetch is a promise I can put in promise array. Maybe there is syntax error in example and I am not sure react native support promise methods fully but here is documentation for promise method I use https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all