Android: upload file with filling out POST body together

Marek Sebera picture Marek Sebera · Jan 22, 2012 · Viewed 14.3k times · Source

I do use MultipartEntity to send File to server, it appears correctly in $_FILES superglobal

But I need also fill in POST body to be read via php://stdin

How can I do that?

current snippet below:

ByteArrayOutputStream bos = new ByteArrayOutputStream(); // stream to hold image
bm.compress(CompressFormat.JPEG, 75, bos); //compress image
byte[] data = bos.toByteArray(); 
HttpClient httpClient = new DefaultHttpClient();
HttpPost postRequest = new HttpPost("REMOTE ADDRESS");
ByteArrayBody bab = new ByteArrayBody(data, "image.jpg");
MultipartEntity reqEntity = new MultipartEntity(
HttpMultipartMode.BROWSER_COMPATIBLE); // is this one causing trouble?
reqEntity.addPart("image", bab); // added image to request
// tried this with no luck
// reqEntity.addPart("", new StringBody("RAW DATA HERE")); 
postRequest.setEntity(reqEntity); // set the multipart entity to http post request
HttpResponse response = httpClient.execute(postRequest);

MultipartEntity is part of HttpMime 4.1.2 API, documentation

Similar to this: Android: Uploading a file to a page along with other POST strings

Answer

Alexandru Mincu picture Alexandru Mincu · Jan 25, 2012

Just add a a few FormBodyPart to your MultipartEntity.

You can use the StringBody object to provide the value.

Here is an example of how you can use it:

byte[] data = {10,10,10,10,10}; 
HttpClient httpClient = new DefaultHttpClient();
HttpPost postRequest = new HttpPost("server url");
ByteArrayBody bab = new ByteArrayBody(data, "image.jpg");
MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
reqEntity.addPart("image", bab);

FormBodyPart bodyPart=new FormBodyPart("formVariableName", new StringBody("formValiableValue"));
reqEntity.addPart(bodyPart);
bodyPart=new FormBodyPart("formVariableName2", new StringBody("formValiableValue2"));
reqEntity.addPart(bodyPart);
bodyPart=new FormBodyPart("formVariableName3", new StringBody("formValiableValue3"));
reqEntity.addPart(bodyPart); 
postRequest.setEntity(reqEntity);
HttpResponse response = httpClient.execute(postRequest);
BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line = null;
while((line = in.readLine()) != null) {
    System.out.println(line);
}

Here is the output of the PHP script:

$_FILES
Array
(
    [image] => Array
        (
            [name] => image.jpg
            [type] => application/octet-stream
            [tmp_name] => /tmp/php6UHywL
            [error] => 0
            [size] => 5
        )

)
$_POST:
Array
(
    [formVariableName] => formValiableValue
    [formVariableName2] => formValiableValue2
    [formVariableName3] => formValiableValue3
)

This doesn't compress all the post vars inside of one content body part but it dose the job.

You can't access the data from php://stdin or $HTTP_RAW_POST_DATA, both are unavailable for multipart/form-data encoding. From the PHP docs:

php://input is a read-only stream that allows you to read raw data from the request body. In the case of POST requests, it is preferable to use php://input instead of $HTTP_RAW_POST_DATA as it does not depend on special php.ini directives. Moreover, for those cases where $HTTP_RAW_POST_DATA is not populated by default, it is a potentially less memory intensive alternative to activating always_populate_raw_post_data. php://input is not available with enctype="multipart/form-data".

Even if you set always_populate_raw_post_data to On it still won't fix the problem:

Always populate the $HTTP_RAW_POST_DATA containing the raw POST data. Otherwise, the variable is populated only with unrecognized MIME type of the data. However, the preferred method for accessing the raw POST data is php://input. $HTTP_RAW_POST_DATA is not available with enctype="multipart/form-data".

My best guess is just add all the data as a ByteArrayBody or StringBody and just use that as if you were reading from php://stdin

Here is an example of ByteArrayBody:

String testString="b=a&c=a&d=a&Send=Send";
reqEntity.addPart(new FormBodyPart("formVariables", new ByteArrayBody(testString.getBytes(), "application/x-www-form-urlencoded", "formVariables")));

And in PHP:

var_dump(file_get_contents($_FILES['formVariables']['tmp_name']));

You should get:

string(21) "b=a&c=a&d=a&Send=Send"

Edit: After some second thoughts I think it's better to just use one StringBody and put all the data in one post variable, then parse it from that, it skips writing the data to a file and deleting it after the request since the temp file is totally useless this will increase performance. Here is an example:

String testString="b=a&c=a&d=a&Send=Send";
bodyPart=new FormBodyPart("rawData", new StringBody(testString));
reqEntity.addPart(bodyPart);

Then from PHP:

var_dump($_POST['rawData']);