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

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.

Related

Upload Image With Expo & Fetch

I am trying to upload an image from my react native app.
If I use my local node server and run this code:
var fs = require("fs");
var options = {
method: "POST",
url: "my_URL",
headers: {},
formData: {
file: {
value: fs.createReadStream("../../assets/image.png"),
options: {
filename: "image.jpg",
contentType: null
}
}
}
};
request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body);
});
I have a succesful upload.
However, with the URI from that we get through the the app, it does not work:
Here is my code on React Native Expo:
const body = new FormData();
body.append("file", 'file:///path/to/file/image123.jpg');
fetch(url, {
method: "POST",
body,
headers: {
"content-type": "multipart/form-data"
}
})
.then(response => {
console.log(response, "RESPONSE");
})
.then(result => {
console.log(result, "RESULT");
})
.catch(error => {
console.log(error, "ERROR");
});
I am unable to get it to work. I think it has something to do with the file path from the device.
Any help will be appreciated.
try to create FormData using this function
const createFormData = (uri) => {
const fileName = uri.split('/').pop();
const fileType = fileName.split('.').pop();
const formData = new FormData();
formData.append('file', {
uri,
name: fileName,
type: `image/${fileType}`
});
return formData;
}
if it doesn't work check permissions

React native im trying to upload image everytime localuri.slpit not defined showing and {_parts:[[]]} and why this _parts coming while sending data

can anyone tell me what wrong with this code im trying to upload image using react-native-image-picker in react native.but it says localUri.split is not defined and sending data shows in inspect element as {_parts:[[]]} and why this _parts coming every post method ...please help me to figure out this..
const takeAndUploadPhotoAsync = async () => {
const token = await AsyncStorage.getItem("userToken");
let result = await launchImageLibrary();
if (result.cancelled) {
return;
}
let localUri = result.uri;
let filename = localUri.split('/').pop().split('#')[0].split('?')[0]
let match = /\.(\w+)$/.exec(filename);
let type = match ? `image/${match[1]}` : `image`;
const url = `/auth/upload-prescription`;
let formData = new FormData();
formData.append("file", { uri: localUri, name: filename, type });
setLoading(true);
const response = await api
.post(url, formData, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'multipart/form-data',
},
})
.then((res) => {
showMessage({
message: "Your Prescription is Uploaded Successfully",
textStyle: {textAlign:'center'},
type: "success",
backgroundColor: "#202877",
});
})
.catch((error) => {
console.log(error.response);
});
dispatch({
type: "TAKE_AND_UPLOAD_PHOTO_ASYNC",
payload: response,
});
setLoading(false);
};

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: Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'. at new ApolloError

I am trying to upload image from my react native app to graphql by using Apollo client with createUploadLink(). When I am trying to mutate data by passing a ReactNativeFile as a variable, then it says
"network request failed: Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'. at new ApolloError ".
This this the mutation which i am trying to use
mutation publishPost(
$content: String!
$LocationInput: LocationInput!
$InputPostAttachment: [InputPostAttachment!]
) {
publishPost(
content: $content
location: $LocationInput
attachments: $InputPostAttachment
) {
content
}
}
InputPostAttachment has type
type InputPostAttachment {
type: PostAttachmentType!
file: Upload!
}
Apollo client settings and i am using apollo-upload-client
const httpLink = createUploadLink({
uri: 'http://localhost:8000/graphql',
});
const authLink = setContext(async (headers: any) => {
const token = await getToken();
return {
...headers,
headers: {
authorization: token ? `Bearer ${token}` : null,
},
};
});
const link = authLink.concat(httpLink);
// create an inmemory cache instance for caching graphql data
const cache = new InMemoryCache();
// instantiate apollo client with apollo link instance and cache instance
export const client = new ApolloClient({
link,
cache,
});
File upload Function and i am using react-native-image-crop-picker for multi image selection
const [image, setimage] = useState([]);
const _pickImage = () => {
ImagePicker.openPicker({
includeBase64: true,
multiple: true,
}).then((images: any) => {
let imageData: any = [];
images.map((data: any) => {
const file = new ReactNativeFile({
uri: data.path,
name: data.filename,
type: data.mime,
});
imageData.push({
type: 'IMAGE',
file: file,
});
});
setimage(imageData);
console.log(images);
});
};
const handlePost = async () => {
const InputPostAttachment: any = [...image];
const LocationInput = {
place: place,
vicinity: vicinity,
province: province,
};
publishPost({variables: {content, LocationInput, InputPostAttachment}})
.then(({data}) => {
console.log(data);
props.navigation.navigate('Home');
})
.catch((err) => {
console.log('err happened');
console.log(err);
});
};
could someone please help me out from this?
In addition to the chrome debugger issue, this error also happens on the expo web.
To anyone uploading images on expo web (or react-native web), here's a working solution:
/** Load image from camera/roll. */
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
quality: 1,
});
if (result.cancelled) {
return;
}
/** web platform: blob. */
const convertBase64ToBlob = async (base64) => {
const response = await fetch(base64);
const blob = await response.blob();
return blob;
};
/** android/ios platform: ReactNativeFile.*/
const createReactNativeFile = (uri) => {
const file = new ReactNativeFile({
uri,
type: mime.lookup(uri) || 'image',
name: `file-${Date.now()}`,
});
return file;
};
/** Use blob for web, ReactNativeFile otherwise. */
const file = Platform.OS === 'web'
? await convertBase64ToBlob(result.uri)
: createReactNativeFile(result.uri);
/** Upload image with apollo. */
mutate({ variables: { file } });
On the web platform, ImagePicker returns a base64 value instead of a file path. This problem doesn't happen if the platform is Android or iOS, as ImagePicker returns a file path, which is expected by apollo-upload-client.
The solution is to detect if the URI is base64 (which happens when the platform is "web") and convert it to a blob.
My apollo-client was configured using apollo-boost and i was using chrome debugger to intercept the network was causing me this issue.
To be more specific I was using the below code to get the network requests sent by my app in the chrome debugger
global.XMLHttpRequest =
global.originalXMLHttpRequest || global.XMLHttpRequest;
global.FormData = global.originalFormData || global.FormData;
if (window.FETCH_SUPPORT) {
window.FETCH_SUPPORT.blob = false;
} else {
global.Blob = global.originalBlob || global.Blob;
global.FileReader = global.originalFileReader || global.FileReader;
}
apollo-upload-client wont send the data in multipart data if we are using chrome debugger. We will face network issue.This issue has the answer. or I had not removed apollo-boost and some part of my app was using it that was also a issue.

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

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.