Send binary data as file to API using Expo - react-native

I would like to know if there is any way to send binary data that is not an image or a video in Expo to an API expecting a file as part of the FormData received.
The API expects binary data so I cannot allow myself to convert it to base64 like people advice to do.
This is what I use today, the server does not recognize a file field.
const form = new FormData();
form.append('url', downloadurl);
form.append('pageUrl', payload.link);
form.append('name', payload.name);
form.append('file', new Blob([buffer]), 'file');
const res = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
},
body: form,
});
EDIT
The following snippet seems to send the blob to the backend. However, the blob size is nearly double the size of the array, which results in the file being received corrupted.
const blob = new Blob([buffer.raw], {
type: 'application/octet-stream',
});
// Here it logs blob with 47000 size, 25330, 25330
console.log(blob, buffer.raw.length, buffer.raw.byteLength);
const url = URL.createObjectURL(blob);
const file = {
uri: url,
type: 'application/octet-stream',
name: 'torrent',
};
const formData = new FormData();
formData.append('torrent', file as any);
formData.append('url', downloadurl);
formData.append('pageUrl', payload.link);
formData.append('name', payload.name);
const xhr = new XMLHttpRequest();
xhr.open('POST', endpoint);
xhr.onload = () => {
console.log('DONE', xhr.status);
};
xhr.send(formData);

Related

How to use Nuxt 3 server as a passthrough API with FormData to hide external endpoints

I'm trying to get my head around the Nuxt /server API and can't seem to figure out how to send a POST request with form-data (ie files) to Nuxt server to forward on to an external service:
In my pages.vue file I have this method:
async function onSubmit() {
const formData = new FormData();
for (let file of form.files) {
await formData.append("image", file);
}
await $fetch("/api/send", {
method: "POST",
body: formData
});
}
and then in /server/api/send.js I have:
export default defineEventHandler(async (event) => {
const { method } = event.node.req;
// I THINK THE ISSUE IS HERE
const body =
method !== "GET" && method !== "HEAD"
? await readMultipartFormData(event)
: undefined;
const response = await $fetch.raw(https://*******, {
method,
baseURL: *********,
headers: {
},
body: body
});
return response._data;
}
I'm effectively creating a passthrough API using Nuxt so that the external endpoint isn't exposed to the end user. Just can't figure out how to access the formData in the correct format to pass through on the server side. I don't think I am supposed to use readMultipartFormData() because that seems to be parsing the data somehow whereas I just want to pass the formData straight through to the external API. Any tips?
I've tried using both readMultipartFormData() and readBody() and neither seem to work. I don't actually need to read the body but rather get it and pass it through without any formatting...
If you want to pass the data with formdata to the endpoint try this library:
https://www.npmjs.com/package/object-to-formdata
code:
import { serialize } from 'object-to-formdata';
const formData = serialize(body);
const response = await $fetch.raw(https://*******, {
method,
baseURL: *********,
headers: {
},
body: formData
});
I managed to make it work with ugly solution, first you have to update nuxt to version 3.2.0 min then here my front side
let jobApplicationDTO = {
firstName: values.firstName,
lastName: values.lastName,
email: values.email,
phoneNumber: values.phoneNumber,
company: values.company,
shortDescription: values.shortDescription
};
const formData = new FormData();
formData.append("application", new Blob([JSON.stringify(jobApplicationDTO)], {type: "application/json"}));
formData.append("file", values.file) ;
//formData.append("file", values.file );
await useFetch("/api/application", {
method: "POST",
body: formData,
onResponse({request, response, options}) {
// Process the response data
if (response.status === 200) {
errorMessage.value = "";
successMessage.value = "Your application wa sent successfully, you will be contacted soon !";
}
},
onResponseError({request, response, options}) {
console.debug(response);
if (response.status === 400) {
successMessage.value = "";
errorMessage.value = "There may be an issue with our server. Please try again later, or send an email to support#mantiq.com";
} else {
successMessage.value = "";
errorMessage.value = "Sorry we couldn’t send the message, there may be an issue with our server. Please try again later, or send an email to support#mantiq.com";
}
},
});
}
and server side
import {FormData} from "node-fetch-native";
export default defineEventHandler(async (event) => {
const {BACKEND_REST_API, ENQUIRY_TOKEN} = useRuntimeConfig();
//retrieve frontend post formData
const form = await readMultipartFormData(event);
const applicationUrl = BACKEND_REST_API + '/job/apply'
console.log("url used for enquiry rest call :" + applicationUrl);
console.log("Job application token :" + ENQUIRY_TOKEN);
const formData = new FormData();
console.log(form);
if (form) {
formData.append(form[0].name, new Blob([JSON.stringify(JSON.parse(form[0].data))], {type: form[0].type}));
formData.append(form[1].name, new Blob([form[1].data], {type: form[1].type}), form[1].filename);
}
console.log(formData.values);
return await $fetch(applicationUrl, {
method: "POST",
body: formData,
headers: {
Authorization: ENQUIRY_TOKEN,
},
});
})
What is funny is on frontend you have to create a formData , then to get content and to recreate a formData from your previous formData converted in MultiFormPart[], i created a ticket on nuxt to see how to do it properly

React native content uri to base64 string

I'm trying to upload files using RN Document Picker.
Once I get those files selected, I need to turn them to base64 string so I can send it to my API.
const handlePickFiles = async () => {
if (await requestExternalStoreageRead()) {
const results = await DocumentPicker.pickMultiple({
type: [
DocumentPicker.types.images,
DocumentPicker.types.pdf,
DocumentPicker.types.docx,
DocumentPicker.types.zip,
],
});
const newUploadedFile: IUploadedFile[] = [];
for (const res of results) {
console.log(JSON.stringify(res, null, 2));
newUploadedFile.push({
name: res.name,
type: res.type as string,
size: res.size as number,
extension: res.type!.split('/')[1],
blob: res.uri, <<-- Must turn this in base64 string
});
}
setUploadedFiles(newUploadedFile);
console.log(newUploadedFile);
}
}
};
The document picker returns content uri (content://...)
They lists this as an example of handling blob data and base64:
let data = new FormData()
data.append('image', {uri: 'content://path/to/content', type: 'image/png', name: 'name'})
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
},
body: data
})
Where they basically say that you don't need to use blob or base64 when using multipart/form-data as content type. However, my graphql endpoint cannot handle multipart data and I don't have time to rewrite the whole API. All I want is to turn it to blob and base64 string, even if other ways are more performant.
Searching for other libraries, all of them are no longer maintained, or has issues with new versions of android. RN Blob Utils is the latest npm that was no longer maintained.
I tried to use RN Blob Utils but I either get errors, wrong data type, or the file uploads but is corrupted.
Some other things I found is that I can use
fetch(res.uri).then(response => {response.blob()})
const response = await ReactNativeBlobUtil.fetch('GET', res.uri);
const data = response.base64();
ReactNativeBlobUtil.wrap(decodeURIComponent(blob))
///----
const blob = ReactNativeBlobUtil.fs.readFile(res.uri, 'base64');
But I can't do anything with that blob file.
What is the simplest way to uplaod files from document picker as base64 format? Is it possible to avoid using external storage permission?
You don't need to the third-party package to fetch BLOB data
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", "[LOCAL_FILE_PATH]", true);
xhr.send(null);
});
// Code to submit blob file to server
// We're done with the blob, close and release it
blob.close();
I ended up using react-native-blob-util
const res = await DocumentPicker.pickSingle({
type: [
DocumentPicker.types.images,
DocumentPicker.types.pdf,
DocumentPicker.types.docx,
DocumentPicker.types.zip,
],
});
const newUploadedFile: IUploadedFile[] = [];
const fileType = res.type;
if (fileType) {
const fileExtension = fileType.substr(fileType.indexOf('/') + 1);
const realURI = Platform.select({
android: res.uri,
ios: decodeURI(res.uri),
});
if (realURI) {
const b64 = await ReactNativeBlobUtil.fs.readFile(
realURI,
'base64',
);
const filename = res.name.replace(/\s/g, '');
const path = uuid.v4();
newUploadedFile.push({
name: filename,
type: fileType,
size: res.size as number,
extension: fileExtension,
blob: b64,
path: Array.isArray(path) ? path.join() : path,
});
} else {
throw new Error('Failed to process file');
}
} else {
throw new Error('Failed to process file');
}

React Native uploading file to multer, i am getting empty buffer

I am making an App with React Native and back end with NodeJS. From VueJS it is exactly the same code(except i am getting the file from an input), and its working fine from an Input and Postman, but i am having trouble on React Native
Code in the App:
const formdata = new FormData()
const file = myMainImage
console.log(file)
const fileName = file.split("/").reverse()[0];
formdata.append('media', {
url: file,
name: fileName,
type: 'image/jpeg'
})
await profileApi.uploadMyMainImage(formdata)
And the request to the backend (tried with axios and fetch)
const postFormMethodWithAuthorization = async (url, content) => {
const headers = getHeaderWithAuthorizationForm()
const response = await Axios.post(url, content, {headers} )
return response.data
}
const postFileusingFetch = async (url, content) => {
const result = await fetch(url, {
method: 'POST',
headers: new Header(await getHeaderWithAuthorizationForm()),
body: content
})
return await result.json()
}
but in the back end i am always getting from req.file:
{
fieldname: 'media',
originalname: 'DADE2091-0C50-456B-8F89-408CCAD98E02.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
buffer: <Buffer >,
size: 0
}
Any Ideas? i thought it can be something related to the file being upload, so i treated it like a stream, but same problem happens, also i am uploading it into AWS S3, but when i get it back the file is empty and cant be opened.
The image uri is being taken from the Camera. I also tried removing file:// with no luck.
Any Help appreciated!

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'
}
})

Trying to extract text through OCR space API in react native but receiving an error as incorrect base64 format

After clicking an image through android mobile and extracting base64 from the image, I am sending it to the OCR space API to extract the text in the image.
getTextByURL is subjected to call the API after receiving the base64.
The format should be data:image/png;base64,iVBORw0KGgoAAAANS....
To obtain the format I am appending the base64 code to 'data:image/png;base64,' which should satisfy the required POST call conditions.
But the error still persists, I could not find whether the error is in the format or in the base64 code.
When I give a base64 code of an image in the POST call instead of base64 generated from the clicked image, the API call is working.
So no idea where is the glitch.
please help me!
getTextByUrl(base) {
var url = "https://api.ocr.space/parse/image";
var subscriptionKey = "xxxxxxxxxxxxxx";
var data = new FormData();
data.append("apikey", subscriptionKey);
data.append("language", "eng");
data.append("isOverlayRequired", "true");
data.append("Base64Image", base)
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
alert(this.responseText);
}
});
xhr.open("POST", "URL");
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.send(data);
}
showCamera() {
this.setState({
showComponent: true,
uri: ""
})
}
The following function is to click the image and extract the base64 from the image.
takePicture() {
const options = {};
//options.location = ...
this.camera.capture({ metadata: options })
.then((data) => {
let pathToImage = data.path;
CompressImage.createCompressedImage(pathToImage, 'compress/images').then(
ImgToBase64.getBase64String(pathToImage)
.then(base64String => {
**base64String = "data:image/jpg;base64," + base64String;**//This gives the base64 of the image
alert(base64String.substring(0, 50) + typeof (base64String));
this.setState({
baa: base64String.substring(0, 200)
})
this.getTextByUrl(base64String)
})
.catch(err => alert(err))
)
})
.catch(err => console.error(err));
}
Thanks in advance.
May be replace "Base64Image" with "base64Image". This works for me
const data = await this.camera.takePictureAsync(options);
const base64Str = 'data:image/jpg;base64,'+data.base64; var data = new FormData();
data.append('apikey', key)
data.append('base64Image', base64)
const headers = {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data;',
}
const config = {
method: 'POST',
headers,
body: data
};
const URL = 'https://api.ocr.space/parse/image';
fetch(URL, config )