简体   繁体   中英

HttpUrlConnection and file issue

Sorry to bring this again. When I first started to develop my android app, I was using HttpClient. I realized that Google officially made this class deprecated for that latest version of Android; they encourage developer to use HttpUrlConnection instead.

I recently faced a wall when I tried to upload a file(JPEG) to my PHP Server API hosted on GAE. I put a lot of effort into using Multipart to achieve that. I ended up using the following class. But it's still not working for some unknown reason. My PHP Server API successfully receives my POST data but can't receive my file. What`s wrong with my code?

In case you're wondering, I can verify that it has nothing to do with my server by using isset($_FILES["profile_picture"]) to verify that I've received it.

Here is my class:

package net.XXXXX;

import android.util.Log;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.HttpsURLConnection;


public class MultipartUtility {

    private HttpURLConnection mConnection;
    private OutputStream outputStream;
    private PrintWriter mWriter;
    private final String boundary;

    private static final String LINE_FEED = "\r\n";
    private final static String CHARSET = "UTF-8";
    private final static String API_URL = "https://select-connection.appspot.com/";
    public static final String LOG_TAG = "APIRequestHandler";
    public static final int CONNECT_TIMEOUT = 15000;
    public static final int READ_TIMEOUT = 10000;

    /**
     * ...
     */
    public MultipartUtility() throws IOException {

        // creates a unique boundary based on time stamp.
        boundary = "===" + System.currentTimeMillis() + "===";

        URL url = new URL(API_URL);
        mConnection = (HttpsURLConnection)url.openConnection();
        mConnection.setConnectTimeout(CONNECT_TIMEOUT);
        mConnection.setReadTimeout(READ_TIMEOUT);
        mConnection.setRequestMethod("POST");
        mConnection.setRequestProperty("Accept-Charset", "UTF-8");
        mConnection.setRequestProperty("Accept", "application/json");
        mConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
        mConnection.setUseCaches (false);
        mConnection.setDoInput(true);
        mConnection.setDoOutput(true);

        outputStream = mConnection.getOutputStream();
        mWriter = new PrintWriter(new OutputStreamWriter(outputStream, CHARSET), true);
    }

    /**
     * ...
     */
    public void addFormField(String name, String value) {
        mWriter.append("--" + boundary).append(LINE_FEED)
                .append("Content-Disposition: form-data; name=\"" + name + "\"")
                .append(LINE_FEED)
                .append("Content-Type: text/plain; charset=" + CHARSET)
                .append(LINE_FEED)
                .append(LINE_FEED)
                .append(value)
                .append(LINE_FEED);
        mWriter.flush();
    }

    /**
     * ...
     */
    public void addFilePart(String fieldName, File uploadFile)
            throws IOException {
        String fileName = uploadFile.getName();
        mWriter.append("--" + boundary).append(LINE_FEED)
                .append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"")
                .append(LINE_FEED)
                .append("Content-Type: " + URLConnection.guessContentTypeFromName(fileName))
                .append(LINE_FEED)
                .append("Content-Transfer-Encoding: binary")
                .append(LINE_FEED)
                .append(LINE_FEED);
        mWriter.flush();

        FileInputStream inputStream = new FileInputStream(uploadFile);
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
        outputStream.flush();
        inputStream.close();

        mWriter.append(LINE_FEED);
        mWriter.flush();
    }

    /**
     * ...
     */
    public void addHeaderField(String name, String value) {
        mWriter.append(name + ": " + value).append(LINE_FEED);
        mWriter.flush();
    }

    /**
     * ...
     */
    public String finish() throws IOException {
        String response;

        mWriter.append(LINE_FEED).flush();
        mWriter.append("--" + boundary + "--").append(LINE_FEED);
        mWriter.close();

        // checks server's status code first
        int status = mConnection.getResponseCode();
        if (status == HttpURLConnection.HTTP_OK) {

            response = ApiRequestHandler.convertStreamToString(new BufferedInputStream(mConnection.getInputStream()));

            mConnection.disconnect();
        } else {
            throw new IOException("Server returned non-OK status: " + status);
        }

        return response;
    }
}

And this is how I use it:

try {
                MultipartUtility connection = new MultipartUtility();
                connection.addFormField("action", "update_picture");
                connection.addFormField("user_id", mUserId);
                connection.addFormField("session_token", mSessionToken);
                connection.addFilePart("profile_picture", mProfilePicture);
                String result = connection.finish();
                Log.d("PROFILE PICTURE", result);
            } catch (IOException e) {
                e.printStackTrace();
            }

Is multipart required? In case you only need to send one file with the POST request, you can simply write its binary data to the output stream of the connection. (I have no experience with PHP but I assume that PHP will be able to read the request body). The other fields could be transferred as query parameters or HTTP headers.

Also I recommend to route the HTTP traffic through a logging proxy like Fiddler2 to see what actually is in the HTTP request body (and compare it with a working test example).

I finally found the problem. Thank you mjn for providing me the Fiddler2 Web Debugging solution. Using this software, I was able to compose a valid HTTP request to test my server API. The problem had nothing to do with my code. The reason why PHP couldn't receive the uploaded files is because Google App Engine automatically refuse every uploaded file. In order to upload a file, you need to create an upload URL first.

Source: https://cloud.google.com/appengine/docs/php/googlestorage/user_upload

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM