How do I upload both form data and multiple files from my android app, using HTTPURLConnection to my server, where I am using PHP?

StackOverflow https://stackoverflow.com/questions/18598235

Question

I have an android application that is sending 1 or more zip files to the server. I'd like to send some additional form data along with the files, because the form data will be used to determine the disposition of the files.

I've been able to upload the files using multi-part as a content-type, but I can't seem to get the php on the server side to see both form data and the files.

What I'd like to know is how to send form data and a list of files to the server using HTTPUrlConnection on the android side and PHP on the server side.

I believe my problem is related to Content-Type and Content-Disposition. I'd really appreciate it if someone could point me at a definitive example, or explain what I'm doing wrong.

Thanks for taking the time to look at this problem.'

Here's the latest, revised android code:

String postDriverUpload(File[] fileList) {
        HttpURLConnection connection = null;
        DataOutputStream outputStream = null;
        String tag = TAG + "postDriverInfo()";
        URL url = null;
        String result = OK_TO_CONTINUE;

        String targetUrl = server + "/upload.php";

        try {
            url = new URL(targetUrl);
        } catch (MalformedURLException e) {
            result = "ERROR: [" + targetUrl + "] can't be parsed into a URL.";
            Log.e(tag, result, e);
            return result;
        }

        try {
            connection = (HttpURLConnection) url.openConnection();
        } catch (IOException e) {
            result = "ERROR: IOException.";
            Log.e(tag, result, e);
            return result;
        }

        // Allow Inputs and Outputs
        connection.setDoInput(true);
        connection.setDoOutput(true);
        connection.setUseCaches(false);

        // Enable POST method

        try {
            connection.setRequestMethod("POST");
        } catch (ProtocolException e) {
            result = "Error: Protocol Exception.";
            Log.e(tag, result, e);
            return result;
        }

        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setRequestProperty("Content-Type",
                "multipart/form-data;boundary=" + BOUNDARY);

        // Encode driver userName
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        try {
            nameValuePairs.add(new BasicNameValuePair("userName", driver
                    .getUserName()));
            Log.d(tag, "Encoded userName='driver' is: "
                    + encodeData(nameValuePairs));

            // Create output Stream to send data to server
            outputStream = new DataOutputStream(connection.getOutputStream());
            writeBoundary(outputStream);
            outputStream
                    .writeBytes("Content-Disposition: form-data; name=\"userName\" "
                            + EOL + EOL);
            outputStream.writeBytes(driver.getUserName() + EOL);
            writeBoundary(outputStream);

            // Starting the Files part
            outputStream
                    .writeBytes("Content-Disposition: form-data; name=\"files\" "
                            + EOL + EOL);
            outputStream.writeBytes("Content-Type: multipart/mixed; boundary="
                    + FILE_BOUNDARY + EOL);
            // Adding the file parts
            for (File file : fileList) {
                writeFileBoundary(outputStream);
                // write out file
                String contentType = String.format(
                        "Content-Disposition:file; filename=\"%s\"" + EOL
                                + "Content-Type: application/x-zip-compressed",
                        file.getName())
                        + EOL + EOL;
                Log.d(tag, "Content-Type = " + contentType);
                outputStream.write(contentType.getBytes());
            }
            writeFileBoundary(outputStream);
            writeBoundary(outputStream);
            outputStream.flush();
            outputStream.close();
        } catch (UnsupportedEncodingException e) {
            result = "Error: UnsupportedEncodingException ";
            Log.e(tag, result, e);
            return result;
        } catch (IOException e) {
            result = "Error: IOException ";
            Log.e(tag, result, e);
            return result;
        }

        // Get Response from server (code and message)
        try {
            int serverResponseCode = connection.getResponseCode();
            String serverResponseMessage = connection.getResponseMessage();
            Log.d(tag, "Response Code = " + serverResponseCode);
            Log.d(tag, "Response Message = " + serverResponseMessage);
        } catch (IOException e) {
            result = "Error getting response from server.";
            Log.e(tag, result, e);
            return result;
        }
        final StringBuilder out = new StringBuilder();
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream()));
            String line;
            while (null != (line = in.readLine())) {
                if (line.contains("ERROR")) {
                    Log.d(tag, "HTTPResponse: " + out.toString());
                    result = "Error: HTTPResponse: " + out.toString();
                    break;
                }
                out.append(line + "\n\n");
            }
            Log.d(tag, "HTTPResponse: " + out.toString());

        } catch (IOException e) {
            Log.w(tag, "WARNING: ", e);
        }

        return result;
    }


void writeBoundary(DataOutputStream out) throws IOException {
    final String twoHyphens = "--";
    out.writeBytes(twoHyphens + BOUNDARY + EOL);
}

void writeFileBoundary(DataOutputStream out) throws IOException {
    final String twoHyphens = "--";
    out.writeBytes(twoHyphens + FILE_BOUNDARY + EOL);
}

Here's the output of my logging on the Android side. As you can see, the key, userName and value, rbenjamin, are being properly loaded into the $_POST variable, but something isn't right past that point. I delimit the next section with the BOUNDARY, but it seems to be gobbled up into the value for the variable 'files'. I'm close, but I'm still missing something. (I stripped out most of the noise in the log file below.)

: Upload Button Clicked.
: Compiling upload file
: getting delivered SQL = SELECT * FROM deliveryorder WHERE delivereddatetime != ''
: uploadEnabled = true
: downloadEnabled = false
: driverEmail = ray.benjamin@gmail.com
: Starting, getting database.
: Found 1 drivers.
: Processing driver 1
: user name is rbenjamin
: email is ray.benjamin@gmail.com
: Uploading files using HTTP to http://192.168.1.17:8080
: There are 16 files.
: Checking download - enabled? false
: Encoded userName='driver' is: userName=rbenjamin
: Content-Type = Content-Disposition:file; filename="upload20130820_1015.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130820_1048.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_1624.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_1628.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_1645.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_1647.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_1705.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_1709.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_1746.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_1801.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_1804.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_1811.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_1813.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_1821.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130821_2050.zip"

: Content-Type: application/x-zip-compressed

: 

: Content-Type = Content-Disposition:file; filename="upload20130825_1957.zip"

: Content-Type: application/x-zip-compressed

: 

: Response Code = 200
: Response Message = OK
: HTTPResponse: <!DOCTYPE html>
: <html>
: <body>
: POST is defined.<br>Key = userName"_, Value = rbenjamin<br>Key = files"_, Value = Content-Type: multipart/mixed; boundary=**FILE*BOUNDARY***25WLqf@***
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130820_1015.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130820_1048.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_1624.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_1628.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_1645.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_1647.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_1705.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_1709.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_1746.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_1801.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_1804.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_1811.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_1813.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_1821.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130821_2050.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***
: Content-Disposition:file; filename="upload20130825_1957.zip"
: Content-Type: application/x-zip-compressed
: --**FILE*BOUNDARY***25WLqf@***<br>
: Notice: Undefined index: userName in /var/www-pbs/upload.php on line 15
: Call Stack:
:     0.0083     638888   1. {main}() /var/www-pbs/upload.php:0
: User Name is: <br>FILES is defined.<br>
: Notice: Undefined index: file in /var/www-pbs/upload.php on line 24
: Call Stack:
:     0.0083     638888   1. {main}() /var/www-pbs/upload.php:0
: <p>Dumping _FILES</p>
: Notice: Undefined index: file in /var/www-pbs/upload.php on line 34
: Call Stack:
:     0.0083     638888   1. {main}() /var/www-pbs/upload.php:0
: Notice: Undefined index: file in /var/www-pbs/upload.php on line 40
: Call Stack:
:     0.0083     638888   1. {main}() /var/www-pbs/upload.php:0
: Upload: <br>
: Notice: Undefined index: file in /var/www-pbs/upload.php on line 41
: Call Stack:
:     0.0083     638888   1. {main}() /var/www-pbs/upload.php:0
: Type  : <br>
: Notice: Undefined index: file in /var/www-pbs/upload.php on line 42
: Call Stack:
:     0.0083     638888   1. {main}() /var/www-pbs/upload.php:0
: Size  : 0 kB<br>
: Notice: Undefined index: file in /var/www-pbs/upload.php on line 43
: Call Stack:
:     0.0083     638888   1. {main}() /var/www-pbs/upload.php:0
: Stored in: 
: Notice: Undefined index: file in /var/www-pbs/upload.php on line 45
: Call Stack:
:     0.0083     638888   1. {main}() /var/www-pbs/upload.php:0
: Notice: Undefined index: file in /var/www-pbs/upload.php on line 47
: Call Stack:
:     0.0083     638888   1. {main}() /var/www-pbs/upload.php:0
:  already exists.
: </body>
: </html>
: 200
Was it helpful?

Solution

No one answered my question, but I eventually found what I needed here: a www.codejava.net article on sending multipart forms programatically . It was just what the doctor ordered. The article provides you with a class, MultipartUtility, that makes it easy to upload using HTTPURLConnection. Not only is it an answer, but it's a well structured one. I had to tweak it a bit for Android, but not much.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top