React-Native send multipart/form-data through HTTP - android

I have a react-native app that needs to send video/images to my server. I already know that normal posts work but when I attempt to send a formData object, it seems to never leave the phone. Here is my code.
// method = 'POST';
// body = new formData();
// body contains text data and image/video file
const post = (url, body, token, method) => {
let xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.setRequestHeader('Authorization', 'Bearer' + token);
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=---------------------------7692764ac82');
xhr.send(body);
console.log(xhr);
return xhr.response;
}
body is a formData object that contains an image/video. In the object that xhr prints at the console log the _response contains "Binary FormData part needs a content-type header." But it seems I set it correctly right?
Please help, there are other similar questions but I have run out of ideas. I have also tried using fetch with no success.

The error message is not about the content-type header for the request (which you have set), it's about the content-type header for the part (which you did not show, so we have to assume it is missing).
When you add the parts to the FormData, don't forget to include a type. Example for an image:
const body = new FormData();
// ...
body.append('image', {
uri: 'file:///...',
type: 'image/jpeg', // <- Did you miss that one?
name: 'someName',
});
With the type properly set, the React Native runtime should add a content-type header for the part. This is done in FormData.js at line 79 in v0.46.0 (wherein value is the value for your type property):
if (typeof value.type === 'string') {
headers['content-type'] = value.type;
}
So, when type is missing, then content-type header for the part is missing, and then on Android you end up here, where you can see the origin of your error message.
This exact error and root cause is discussed in that GitHub issue.

Related

How to decode json string as UTF-8?

I've been working with json for some time and the issue is the strings I decode are encoded as Latin-1 and I cannot get it to work as UTF-8. Because of that, some characters are shown incorrectly (ex. ' shown as ').
I've read a few questions here on stackoverflow, but they doesn't seem to work.
The json structure I'm working with look like this (it is from YouTube API):
...
"items": [
{
...
"snippet": {
...
"title": "Powerbeats Pro “Totally Wireless” Except when you need a wire",
...
}
}
]
I encode it with:
response = await http.get(link, headers: {HttpHeaders.contentTypeHeader: "application/json; charset=utf-8"});
extractedData = json.decode(response.body);
dataTech = extractedData["items"];
And then what I tried was changing the second line to:
extractedData = json.decode(utf8.decode(response.body));
But this gave me an error about wrong format. So I changed it to:
extractedData = json.decode(utf8.decode(response.bodyBytes));
And this doesn't throw the error, but neither does it fix the problem. Playing around with headers does neither.
I would like the data to be stored in dataTech as they are now, but encoded as UTF-8. What am I doing wrong?
Just an aside first: UTF-8 is typically an external format, and typically represented by an array of bytes. It's what you might send over the network as part of an HTTP response. Internally, Dart stores strings as UTF-16 code points. The utf8 encoder/decoder converts between internal format strings and external format arrays of bytes.
This is why you are using utf8.decode(response.bodyBytes); taking the raw body bytes and converting them to an internal string. (response.body basically does this too, but it chooses the bytes->string decoder based on the response header charset. When this charset header is missing (as it often is) the http package picks Latin-1, which obviously doesn't work if you know that the response is in a different charset.) By using utf8.decode yourself, you are overriding the (potentially wrong) choice being made by http because you know that this particular server always sends UTF-8. (It may not, of course!)
Another aside: setting a content type header on a request is rarely useful. You typically aren't sending any content - so it doesn't have a type! And that doesn't influence the content type or content type charset that the server will send back to you. The accept header might be what you are looking for. That's a hint to the server of what type of content you'd like back - but not all servers respect it.
So why are your special characters still incorrect? Try printing utf8.decode(response.bodyBytes) before decoding it. Does it look right in the console? (It very useful to create a simple Dart command line application for this type of issue; I find it easier to set breakpoints and inspect variables in a simple ten line Dart app.) Try using something like Wireshark to capture the bytes on the wire (again, useful to have the simple Dart app for this). Or try using Postman to send the same request and inspect the response.
How are you trying to show the characters. If may simply be that the font you are using doesn't have them.
just add the header : 'Accept': 'application/json; charset=UTF-8';
it worked for me
My header looks like :
final response = await http.get(url, headers: {
'Content-Type': 'application/json; charset=UTF-8',
'Accept': 'application/json',
'Authorization': 'Bearer $token',
});
And the response is handled like:
Map<String, dynamic> data = json.decode(utf8.decode(response.bodyBytes));

How can I retrieve a file from the file system using React Native, for conversion to base64 and http posting as JSON?

I am using the react native template from this article. The code is all available on Github here.
You can use the app to record audio and save to the file system. I figured out how to retrieve the file URIs, but I'm finding it impossible to get the actual contents of the file itself. All I want to do is retrieve the actual file contents as a binary or ascii or hex string or whatever (it's a .m4a file), so I can convert it to base64 encoding and then post it as JSON to my server. Here's what my code looks like:
/src/screens/RecordAudioScreen/RecordAudioScreenContainer.js
onEndRecording: props => async () => {
try {
// here is the URI
console.log("HERE IS THE URI!!");
const audio_data = "URI: " + props.recording._uri;
// Help needed here
// retrieve file contents from the Android/iOS file system
// Encode as base64
audio_data = ... // ???????
// this works already, posts to the server
fetch("https://myserver.com/accept_file",
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify({user_id: 1, audio_data: audio_data})
})
.then(function(res){ console.log(res) })
.catch(function(res){ console.log(res) });
console.log("FINISHED POST REQUEST!!!")
await props.recording.stopAndUnloadAsync();
await props.setAudioMode({ allowsRecordingIOS: false });
} catch (error) {
console.log(error); // eslint-disable-line
}
if (props.recording) {
const fileUrl = props.recording.getURI();
props.recording.setOnRecordingStatusUpdate(null);
props.setState({ recording: null, fileUrl });
}
},
I've already tried a bunch of stuff with no success. Ideally I just get the file contents from the File system, convert to base64 and post it off all in this method just in javascript, but this is seemingly very difficult for what should be a pretty standard thing to do in an app based framework.
Here's some stack overflow questions on React Native Fetch Blob which I couldn't make work Fetch Blob 1 Fetch Blob 2
I've tried using React Native Fs, but I can't get it to load properly, I got super bogged down in stuff I didn't understand after trying to eject the app. I'd prefer a plain React Native solution if possible.
I've also tried some code using FormData but couldn't get that to work either.
Maybe the answer is kind of like this question about retrieving images from firebase? I don't know, this is my first attempt at doing anything in React.
It might also have something to do with the "file://" prefix in the URI that gets generated because there's a lot of questions discussing removing that (only for Android, or only for iOS I'm not too clear).
Converting to base64 will be done with something like this, but I need the actual file contents first:
Very appreciative of any help.
Some time ago I wrote a simple example of a record voice app.
To get the files I used this method:
import RNFS from 'react-native-fs';
(...)
getFiles() {
RNFS.readDir(AudioUtils.DocumentDirectoryPath)
.then((result) => {
this.setState({recordedFiles: result});
return Promise.all([RNFS.stat(result[0].path), result[0].path]);
})
.catch((err) => {
console.log(err.message, err.code);
});
}
It worked just fine.
Here's the full example https://github.com/soutot/react-native-voice-record-app
Let me know if it helped. Otherwise we can try a different approach

Use fetch to post a blob in react-native

I'm trying to post a blob. It's definitely a blob. This isn't working in react-native though. I'm getting a red screen that says "PUT must have a request body". Well, I've put the blob in the request body.
createAttachment: function(url, blob) {
var settings = {
method: "PUT",
headers: {
'Accept': 'image/jpeg',
'Content-Type': 'image/jpeg',
'If-Match': '*',
},
body: blob
};
return fetch(url, settings)
}
My project had same problem before, according to this issue perhaps, blob data is not supported in react native fetch API currently (Both in send and receive).
So I made a module myself ..
https://github.com/wkh237/react-native-fetch-blob
It works fine in our project, if you don't mind to take a look, it might helps.
Use rn-fetch-blob lib for this:
https://github.com/joltup/rn-fetch-blob#user-content-upload-example--dropbox-files-upload-api

Integrating ajax, jquery based webservices with android

I am trying to consume a webservice currently made in ajax. I have no idea what this web service is actually doing other than it uses POST Data. When i try to see its output on POSTMAN (Rest api client) I am getting errors.
This is the structure of web service :
var request = {};
request.UserName = "some data"; // this should be always hard coded like this
request.Password = "some data"; // this should be always hard coded like this
request.CurrentUsername = "admin"; // this is hard coded like this for now
request.FirstName = "some data";
request.LastName = "some data";
var send = {};
send.request = request;
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "some link",
data: JSON.stringify(send),
dataType: "json",
async: false,
success: function (msg) {
// Process the result here
},
error: function () {
//alert("error");
// Display the error here in the front end
}
});
});
});
I need to get its output in android. Since i have little knowledge in ajax, jquery(backend) and done json parsing(in android) with post method using web service link and parameters. Please guide me how to implement in this case.
Moreover i usually check web service output on postman but here it is giving me bad request every time.
Please help.
have you tried using Fiddler4 by Telerik? i was struggling for 2 weeks at work doing something very similiar to this. I was using webclient to send data from Android to my web page. I got errors non stop. nothing online helped. using fiddler i was able to see what my post data was and where it was and all details. it should help you narrow down a lot of things.
Yes finally got a way to move it. It is very simple and following header is needed in android end thats all
httpPost.setHeader("Content-Type", "application/json");

upload image file from jquery to node.js

I am trying to upload a file from andriod application, using Jquery to node.js using express..
My client side code is:
function uploadData(win) {
var padI = imagedata.length-1
while( '=' == imagedata[padI] ) {
padI--
}
var padding = imagedata.length - padI - 1
var user = load('user')
$.ajax({
url:'http://'+server+'/lifestream/api/user/'+user.username+'/upload',
type:'POST',
contentType: false,
processdata:false,
data:imagedata,
success:win,
error:function(err){
showalert('Upload','Could not upload picture.')
},
})
}
I have used post form without any content type because if i use multipart/form-data it says error about boundary ..
my server side code using node.js is:
function upload(req,res) {
var picid=uuid()
console.log('Got here..' + __dirname)
//console.log('Image file is here ' + req.files.file.path)
// console.log('local name: ' + req.files.file.name)
var serverPath = __dirname+'/images/' + picid+'.jpg'
fs.rename(
req.files.file.path,
serverPath,
function(error) {
if (error) {
console.log('Error '+error)
res.contentType('text/plain')
res.send(JSON.stringify({error: 'Something went wrong saving to server'}))
return;
}
// delete the /tmp/xxxxxxxxx file created during download
fs.unlink(req.files.file.path, function() { })
res.send(picid)
}
)
}
when the file comes to server, it gives an error of res.files.file is undefined ..
I have searched alot of forums, they say that res.files.file is only access when contenttype is multipart/form-data but then the boundary problem occurs
Any help on that is highly appreciated
Boundary is a special sequence of characters that separates your binary data.
You should submit MIME type as multipart/form-data, as well as set your imagedata to FormData() type (from your snippet it's not clear if it is FormData type).
Here are similar issues and solutions:
How to set a boundary on a multipart/form-data request while using jquery ajax FormData() with multiple files
jQuery AJAX 'multipart/form-data' Not Sending Data?
A great alternative to writing this code is to use filepicker.io This allows you to connect to yoru own s3 bucket. When the file is saved, you get back a callback with the S3 url, you can then simply pass that url to your node api, and save it. I have used this to avoid having to write extra server code for handling file uploads. Extra bonus, if you need to do this with images, and want users to be able to edit the images, you can use Aviary which allows an image to be edited locally, and you then get back another s3 url, that you can then save to your server..

Categories

Resources