Send image file using java HTTP POST connections

2019-01-13 19:30发布

问题:

I'm trying to send an image to a website using Java HTTP POST requests.

I'm using the base code used here Upload files from Java client to a HTTP server:

This is my modification:

String urlToConnect = "http://localhost:9000/upload";
File fileToUpload = new File("C:\\Users\\joao\\Pictures\\bla.jpg");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.

URLConnection connection = new URL(urlToConnect).openConnection();
connection.setDoOutput(true); // This sets request method to POST.
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
PrintWriter writer = null;
try {
    writer = new PrintWriter(new OutputStreamWriter(connection.getOutputStream()));
    writer.println("--" + boundary);
    writer.println("Content-Disposition: form-data; name=\"picture\"; filename=\"bla.jpg\"");
    writer.println("Content-Type: image/jpeg");
    writer.println();
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileToUpload)));
        for (String line; (line = reader.readLine()) != null;) {
            writer.println(line);
        }
    } finally {
        if (reader != null) try { reader.close(); } catch (IOException logOrIgnore) {}
    }
    writer.println("--" + boundary + "--");
} finally {
    if (writer != null) writer.close();
}

// Connection is lazily executed whenever you request any status.
int responseCode = ((HttpURLConnection) connection).getResponseCode();
System.out.println(responseCode); // Should be 200

I get a 200 response code in the end, but the image is buggy, as in, random colors, which make me think it's an error in character encoding. I tried using UTF-8 as in the original example, but that just creates a corrupt image.

I am also 100% sure it's not a serverside problem, because I can use rest clients such as Advanced Rest Client/Postman and they can send an image with no problems.

Can you help me pinpoint what's wrong? Thank you.

回答1:

import java.io.File;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;


public class PostFile {
  public static void main(String[] args) throws Exception {
    HttpClient httpclient = new DefaultHttpClient();
    httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);

    HttpPost httppost = new HttpPost("http://localhost:9000/upload");
    File file = new File("C:\\Users\\joao\\Pictures\\bla.jpg"");

    MultipartEntity mpEntity = new MultipartEntity();
    ContentBody cbFile = new FileBody(file, "image/jpeg");
    mpEntity.addPart("userfile", cbFile);


    httppost.setEntity(mpEntity);
    System.out.println("executing request " + httppost.getRequestLine());
    HttpResponse response = httpclient.execute(httppost);
    HttpEntity resEntity = response.getEntity();

    System.out.println(response.getStatusLine());
    if (resEntity != null) {
      System.out.println(EntityUtils.toString(resEntity));
    }
    if (resEntity != null) {
      resEntity.consumeContent();
    }

    httpclient.getConnectionManager().shutdown();
  }
}

Use HttpClient to work out this code. Its always better to use stable libraries other than handling from scratch, unless there is something to be handled in custom way.



回答2:

Reader/Writer classes are designed to handle text data, while images are binary. You need to interpret your files as binary:

FileChannel         in  = new FileInputStream(fileToUpload).getChannel();
WritableByteChannel out = Channels.newChannel(connection.getOutputStream());

in.transferTo(0, fileToUpload.size(), out)

Of course, you still need to close all opened resources.



回答3:

Try that:

private DefaultHttpClient mHttpClient;
Context context;
public String error = "";

//Contrutor para que metodos possam ser usados fora de uma activity
public HTTPconector(Context context) {
    this.context = context;
}


public HTTPconector() {
    HttpParams params = new BasicHttpParams();
    params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
    mHttpClient = new DefaultHttpClient(params);
}


public void FileClientPost(String txtUrl, File file){
    try
    {
        error = "";
        HttpPost httppost = new HttpPost(txtUrl);
        MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
        multipartEntity.addPart("Image", new FileBody(file));
        httppost.setEntity(multipartEntity);
        mHttpClient.execute(httppost, new PhotoUploadResponseHandler());
    }
    catch (Exception e)
    {
        Log.e(HTTPconector.class.getName(), e.getLocalizedMessage(), e);
        e.getStackTrace();
        error = e.getMessage();
    }
}

//Verifica se a rede esta disponível
public boolean isNetworkAvailable() {
    ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = cm.getActiveNetworkInfo();
    // if no network is available networkInfo will be null
    // otherwise check if we are connected
    if (networkInfo != null && networkInfo.isConnected()) {
        return true;
    }
    return false;
}

public String Get(String txtUrl){
    try {
        URL url = new URL(txtUrl);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setReadTimeout(10000);
        con.setConnectTimeout(15000);
        con.setRequestMethod("GET");
        con.setDoInput(true);
        con.connect();

        return readStream(con.getInputStream());

    }  catch (ProtocolException e) {
        e.printStackTrace();
        return "ERRO: "+e.getMessage();
    } catch (IOException e) {
        e.printStackTrace();
        return "ERRO: "+e.getMessage();
    }
}


public String Post(String txtUrl){
    File image;

    try {
        URL url = new URL(txtUrl);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setRequestMethod("POST");
        con.setDoInput(true);
        con.setDoOutput(true);
        con.connect();

        //con.getOutputStream().write( ("name=" + "aa").getBytes());

        return readStream(con.getInputStream());
    } catch (ProtocolException e) {
        e.printStackTrace();
        return "ERRO: "+e.getMessage();
    } catch (IOException e) {
        e.printStackTrace();
        return "ERRO: "+e.getMessage();
    }
}


//Usado para fazer conexão com a internet
public String conectar(String u){
    String resultServer = "";
    try {
        URL url = new URL(u);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        resultServer = readStream(con.getInputStream());
    } catch (Exception e) {
        e.printStackTrace();
        resultServer = "ERRO: "+ e.getMessage();
    }

    Log.i("HTTPMANAGER: ", resultServer);
    return resultServer;
}

//Lê o resultado da conexão
private String readStream(InputStream in) {
    String serverResult = "";
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new InputStreamReader(in));
        String line = "";
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }

        serverResult = reader.toString();
    }   catch (IOException e) {
        e.printStackTrace();
        serverResult = "ERRO: "+ e.getMessage();
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
                serverResult = "ERRO: "+ e.getMessage();
            }
        }
    }
    return  serverResult;
}

private class PhotoUploadResponseHandler implements ResponseHandler<Object>
{
    @Override
    public Object handleResponse(HttpResponse response)throws ClientProtocolException, IOException {

        HttpEntity r_entity = response.getEntity();
        String responseString = EntityUtils.toString(r_entity);
        Log.d("UPLOAD", responseString);
        return null;
    }
}


回答4:

Today I run into the same issue, I wrote a little nodejs server supports just two routes, upload and download images.

Client should be a java class which sends a images payload via HTTP POST multipart/form-data standard to the server.

If you would like to know why HTTP POST multipart/form-data, please check out the answer from Ciro Santilli from this post: What does enctype='multipart/form-data' mean?

Luckily I found this nice and really good example code:

https://www.techcoil.com/blog/how-to-upload-a-file-via-a-http-multipart-request-in-java-without-using-any-external-libraries/

It shows how we can build up the payload of an multipart http body manuelly without any external lib, only little limitation from my perspective is, that it only handle a mulitpart body with one file.


Because I had no HTML page to sniff the generated POST payload, I used python to generate it and sniff it via wireshark.

Python3 code:

import requests
posturl = 'http://<server>:<port>/<path>'
files = {'image' : open('<file>', 'rb')}
r = requests.post(posturl, files = files)

Just for note: if we define the parameter files from the requests lib with an dict, it generates an mulipart/form-data content. http://docs.python-requests.org/en/master/user/advanced/#post-multiple-multipart-encoded-files

Wireshark shows everything very clear and finally I ended up with this for sending java:

HttpURLConnection conn =
        (HttpURLConnection) new URL("http://<server>:<port>/<path>")).openConnection();

// some arbitrary text for multitext boundary
// only 7-bit US-ASCII digits max length 70
String boundary_string = "some radom/arbitrary text";

// we want to write out
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.addRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary_string);

// now we write out the multipart to the body
OutputStream conn_out = conn.getOutputStream();
BufferedWriter conn_out_writer = new BufferedWriter(new OutputStreamWriter(conn_out));
// write out multitext body based on w3 standard
// https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
conn_out_writer.write("\r\n--" + boundary_string + "\r\n");
conn_out_writer.write("Content-Disposition: form-data; " +
        "name=\"image\"; " +
        "filename=\""+ <File class instance>.getName() +"\"" +
        "\r\n\r\n");
conn_out_writer.flush();

// payload from the file
FileInputStream file_stream = new FileInputStream(<File class instance>);
// write direct to outputstream instance, because we write now bytes and not strings
int read_bytes;
byte[] buffer = new byte[1024];
while((read_bytes = file_stream.read(buffer)) != -1) {
conn_out.write(buffer, 0, read_bytes);
}
conn_out.flush();
// close multipart body
conn_out_writer.write("\r\n--" + boundary_string + "--\r\n");
conn_out_writer.flush();

// close all the streams
conn_out_writer.close();
conn_out.close();
file_stream.close();
// execute and get response code
conn.getResponseCode();

To get the response from the POST just read the input stream accessed via getInputStream(), code snipped in the link.