httpclient multipart post with input streaming - android

I'm looking for simple and clean solution for HTTP multipart post, which will send some Strings (form data) and several files with streaming support. (files needs to be streamed to avoid out of memory error)
I'd like to achieve this with the built-in "org.apache.httpclient" if possible.
I was able to create a clean solution with HttpURLConnection.
Despite all my efforts with this solution strings were sent with 8859-x encoding instead of UTF-8.
EDIT: My code is available at MultiPart with HttpURLConnection source
I created an output stream with this code:
HttpURLConnection connection = setupConnection();
dataOutputStream = new DataOutputStream(connection.getOutputStream());
After this I just wrote the data with dataOutputStream.writeBytes
If i could get an outputstream from httpclient it would be great, however it seems it works a different way.
Any help is appreciated.
Thanks

I've just created a simple solution for this: android_multipart_entity.
It's free (including for commercial usage), however if this is possible please keep references to me inside of my classes.
It's designed to be used with the built in Android HttpClient. Sample usage code:
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://www.yourhost.com");
MultipartEntity entity = new MultipartEntity();
entity.addPart(new StringPart("name", "yourname"));
File imageFile = // .. get your image file
entity.addPart(new FilePart("picture", imageFile, null, "image/jpeg"));
httppost.setEntity(entity);
HttpResponse httpResponse = httpclient.execute(httppost);
EDIT:
I reviewed your MultiPart with HttpURLConnection code. You get UTF-8 issue because of DataOutputStream usage. API says for that class:
Wraps an existing OutputStream and writes big-endian typed data to it. Typically, this stream can be read in by DataInputStream.
This class just does not suit your needs. In order to read the data you would have to have its direct opposite - DataInputStream on the other end.
So my advice would be to use plain OutputStream. And write bytes to it. Smth like this:
outputStream.write(partSeparator.getBytes());
outputStream.write(("Content-Disposition: form-data; name=\"" + parameter.getKey() + "\"" + lineEnd).getBytes());
outputStream.write(("Content-Type: text/plain; charset=UTF-8" + lineEnd).getBytes());
outputStream.write(("Content-Transfer-Encoding: 8bit" + lineEnd).getBytes());
outputStream.write(lineEnd.getBytes());
outputStream.write(parameter.getValue().getBytes("UTF-8")); // <= this is it!
outputStream.write(lineEnd.getBytes());
outputStream.write(partSeparator.getBytes());

Related

Decode bitmap from stream without Apache HTTP Client

Until recently, I decoded images from a web resource using the Apache HTTP Client using this code:
HttpGet httpRequest = new HttpGet(params[0].toURI());
HttpClient httpClient = new DefaultHttpClient();
HttpResponse response = httpClient.execute(httpRequest);
HttpEntity entity = response.getEntity();
BufferedHttpEntity bufferedEntity = new BufferedHttpEntity(entity);
return BitmapFactory.decodeStream(bufferedEntity.getContent());
This all worked perfectly fine.
Now with Android 6, Apache HTTP Client has been deprecated. Not to worry, I thought, just use java.net.HttpUrlConnection instead as recommended here:
http://developer.android.com/about/versions/marshmallow/android-6.0-changes.html#behavior-apache-http-client
The code I tried and that I found in other questions here is:
HttpURLConnection connection = (HttpURLConnection) params[0].openConnection();
// connection.setRequestProperty("User-Agent", "");
connection.setRequestMethod("GET");
// connection.setDoInput(true);
connection.connect();
InputStream inputStream = connection.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
inputStream.close();
return bitmap;
This doesn't work. bitmap will always be null for the same image resource that works with the old code.
Does anyone have any insight into this? Here are other questions I tried and why they didn't work:
Android: bitmapfactory.decodestream returns null (Answers use deprecated methods/classes)
Bitmap.decodeStream returns null on specific existing (And working) images (Same as above)
The problem was caused by a simple HTTP/HTTPS issue. The image resource was requested from a http:// address. The server is set up to issue a 307 (temporary redirect) to the matching https:// address.
Although the default of HttpURLConnection is to follow redirects, the code given in the question didn't work. FYI Picasso didn't load the image either.
Requesting the image via its https:// address solved the problem.
Use Volley or Picasso. That's the recommended approach.

Android HttpPut Has No Body, Despite Setting the Entity

I'm trying to PUT some XML to a server, but the gist of it is that no matter what I do, HttpPut simply won't put anything in the Http body. The server always comes back saying that the body is missing, and looking at it through Wireshark, nothing is there! Here's the code I'm using to set up and run the request:
HttpPut putRequest = new HttpPut(urlString]);
StringEntity stringEntity = new StringEntity(xmlString, HTTP.ISO_8859_1);
stringEntity.setContentType("text/xml");
putRequest.setEntity(stringEntity);
putRequest.addHeader("Host", formatUrlForHostHeader(broadsoftUrl));
putRequest.addHeader("Authorization", authorizationString);
putRequest.addHeader("Content-Type", "text/xml");
putRequest.addHeader("Accept", "text/xml");
response = httpClient.execute(putRequest);
I'm not sure what else to include here. I tried it on 4.2 and 4.0.3. This code is running in the doInBackground of an AsyncTask. The response code I get is a 409 Conflict, and the body is the server's application-specific message, telling me the body is missing. I confirmed that it's missing with Wireshark.
EDIT:
An interesting note is that I ran the same code standalone on my desktop, and it worked. So, is there something up with the Android versions of HttpClient, or the system? I tried a few different API levels, too, just to check.
Any thoughts?
Thanks!
Alright, so the solution was to just give up on HttpPut and all that, and use HttpURLConnection. Here's how we ended up doing it:
URL url = new URL(theUrl);
HttpURLConnection httpCon = (HttpURLConnection) url.openConnection();
httpCon.setRequestProperty("Host", formatUrlForHostHeader(broadsoftUrl));
httpCon.setRequestProperty("Authorization", authorizationString);
httpCon.setRequestProperty("Content-Type", "text/xml; charset=ISO_8859_1");
httpCon.setRequestProperty("Accept", "text/xml");
httpCon.setDoInput(true);
httpCon.setDoOutput(true);
httpCon.setRequestMethod("PUT");
OutputStreamWriter out = new OutputStreamWriter(httpCon.getOutputStream(), "ISO_8859_1");
out.write(xmlData);
out.close();
if(httpCon.getErrorStream() == null) {
return "";
} else {
return "ERROR";
}
We didn't need to get the response from our PUT request, but you check if it failed by seeing if the error stream exists. If you wanted to get the response, you would do something like this:
StringWriter writer = new StringWriter();
IOUtils.copy(httpCon.getInputStream(), writer, encoding);
String responseString = writer.toString();
Of course, you would have to include Apache's IOTools in your app.
409 Conflict is usually an Edit Conflict error, usually associated with wikis, but it could be any type of conflict with the request.
http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
What type of data are you trying to post and is it possible that the host has existing data that cannot be changed?

How to handle interruptions during downloading large file in android?

I have to download one large compressed file (contains multiple files) from an FTP server. During downloading, if it is interrupted/paused by the user or network, the broken downloaded file should be saved. Later, user can resume download the same file from where it is broken.
You can use DownloadManager class.
Download manager is a system service that handles long-running HTTP downloads.
Check http://developer.android.com/reference/android/app/DownloadManager.html
Above class available since API level 9.
One can set the range of bytes to download using the Range HTTP header. All you have to do is remember how many bytes you previously downloaded. Using the classes from apache, it would look like this :
DefaultHttpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(youSourceUri);
// That's the important part. Count represents the number of bytes already downloaded.
// We leave the range open, so it will download 'till the end of the file.
httpGet.addHeader(new BasicHeader("Range", "bytes=" + count + "-"));
HttpResponse response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
InputStream inputStream = entity.getContent();
// Then read from the input stream
Remember to add the appropriate try/catch/finally clauses to handle the streams safely and be sure to close them. They were omitted here for readability.

Use Python bottle server to receive FileEntity of a POST request from Android

On Android phone, I used setEntity() to put the FileEntity to the POST request.
HttpPost post = new HttpPost(uri);
FileEntity reqEntity = new FileEntity(f, "application/x-gzip");
reqEntity.setContentType("binary/octet-stream");
reqEntity.setChunked(true);
post.addHeader("X-AethersNotebook-Custom", configuration.getCustomHeader());
post.setEntity(reqEntity);
When using bottle, tried this but it is not working
f = request.body
gzipper = gzip.GzipFile( fileobj= f )
content = gzipper.read()
The content will be an empty string. So I tried to look at request.forms and request.files. Both of them have no key and value.
request.files.keys()
request.forms.keys()
When searching, I read about the entity: "a request MAY transfer entity" and the entity has entity-header and entity-value. So it may be something like file-content = e.get(entity-header).
Using that code, the phone send file using chunked encoding. Because py-bottle does not support chunked enconding, the solution here is rewrite the android to send file as body of POST request.

Android default charset when sending http post/put - Problems with special characters

I have configured the apache httpClient like so:
HttpProtocolParams.setContentCharset(httpParameters, "UTF-8");
HttpProtocolParams.setHttpElementCharset(httpParameters, "UTF-8");
I also include the http header "Content-Type: application/json; charset=UTF-8" for all http post and put requests.
I am trying to send http post/put requests with a json body that contains special characters (ie. chinese characters via the Google Pinyin keyboard, symbols, etc.) The characters appear as gibberish in the logs but I think this is because DDMS does not support UTF-8, as descibed in this issue.
The problem is when the server receives the request, it sometimes doesn't see the characters at all (especially the Chinese characters), or it becomes meaningless garbage when we retrieve it through a GET request.
I also tried putting 250 non-ascii characters in a single field because that particular field should be able to take up to 250 characters. However, it fails to validate at the server side which claims that the 250 character limit has been exceeded. 250 ASCII characters work just fine.
The server dudes claim that they support UTF-8. They even tried simulating a post request that contains Chinese characters, and the data was received by the server just fine. However, the guy (a Chinese guy) is using a Windows computer with the Chinese language pack installed (I think, because he can type Chinese characters on his keyboard).
I'm guessing that the charsets being used by the Android client and the server (made by Chinese guys btw) are not aligned. But I do not know which one is at fault since the server dudes claim that they support UTF-8, and our rest client is configured to support UTF-8.
This got me wondering on what charset Android uses by default on all text input, and if it can be changed to a different one programatically. I tried to find resources on how to do this on input widgets but I did not find anything useful.
Is there a way to set the charset for all input widgets in Android? Or maybe I missed something in the rest client configuration? Or maybe, just maybe, the server dudes are not using UTF-8 at their servers and used Windows charsets instead?
Apparently, I forgot to set the StringEntity's charset to UTF-8. These lines did the trick:
httpPut.setEntity(new StringEntity(body, HTTP.UTF_8));
httpPost.setEntity(new StringEntity(body, HTTP.UTF_8));
So, there are at least two levels to set the charset in the Android client when sending an http post with non-ascii characters.
The rest client itself itself
The StringEntity
UPDATE: As Samuel pointed out in the comments, the modern way to do it is to use a ContentType, like so:
final StringEntity se = new StringEntity(body, ContentType.APPLICATION_JSON);
httpPut.setEntity(se);
I know this post is a bit old but nevertheless here is a solution:
Here is my code for posting UTF-8 strings (it doesn't matter if they are xml soap or json) to a server. I tried it with cyrillic, hash values and some other special characters and it works like a charm. It is a compilation of many solutions I found through the forums.
HttpParams httpParameters = new BasicHttpParams();
HttpProtocolParams.setContentCharset(httpParameters, HTTP.UTF_8);
HttpProtocolParams.setHttpElementCharset(httpParameters, HTTP.UTF_8);
HttpClient client = new DefaultHttpClient(httpParameters);
client.getParams().setParameter("http.protocol.version", HttpVersion.HTTP_1_1);
client.getParams().setParameter("http.socket.timeout", new Integer(2000));
client.getParams().setParameter("http.protocol.content-charset", HTTP.UTF_8);
httpParameters.setBooleanParameter("http.protocol.expect-continue", false);
HttpPost request = new HttpPost("http://www.server.com/some_script.php?sid=" + String.valueOf(Math.random()));
request.getParams().setParameter("http.socket.timeout", new Integer(5000));
List<NameValuePair> postParameters = new ArrayList<NameValuePair>();
// you get this later in php with $_POST['value_name']
postParameters.add(new BasicNameValuePair("value_name", "value_val"));
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(postParameters, HTTP.UTF_8);
request.setEntity(formEntity);
HttpResponse response = client.execute(request);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer sb = new StringBuffer("");
String line = "";
String lineSeparator = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line);
sb.append(lineSeparator);
}
in.close();
String result = sb.toString();
I hope that someone will find this code helpful. :)
You should set charset of your string entity to UTF-8:
StringEntity stringEntity = new StringEntity(urlParameters, HTTP.UTF_8);
You can eliminate the server as the problem by using curl to send the same data.
If it works with curl use --trace to check the output.
Ensure you are sending the content body as bytes. Compare the HTTP request from Android with the output from the successful curl request.

Categories

Resources