20

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.

Community
  • 1
  • 1
asdt11
  • 203
  • 1
  • 3
  • 4
  • Think you should add the parameter `Content-Transfer-Encoding: binary`, since the uploading should be handled in binary form. – Gowtham Dec 01 '14 at 13:52
  • Maybe problem was because you are using BufferedReader.readLine() to get image?? You of course find image "buggy" because you need to use just InputStream.read(bytes, 0, bytes.length) and then outputStream.write(bytes) where outputStream is connection.getOutputStream(); – nick Jul 28 '16 at 08:44

5 Answers5

19
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.

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
Kris
  • 8,680
  • 4
  • 39
  • 67
  • i've got this error: STATUS LINE:HTTP/1.1 200 OK Stampo il response: ERROR: File not moved correctlyArray ( [usr_img] => Array ( [name] => FotoProfilo.png [type] => image/jpeg [tmp_name] => /tmp/phpp170qS [error] => 0 [size] => 5327 ) ) – Lele Aug 07 '13 at 07:32
  • 2
    Note for this to work in build using something like maven you need both httpclient and httpmime dependencies. – Paul Taylor Jul 07 '17 at 11:04
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.

whati001
  • 317
  • 2
  • 9
3

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.

ursa
  • 4,404
  • 1
  • 24
  • 38
  • You probably mean WritableByteChannel, without the extra 'e'. java.nio.channels.WritableByteChannel – Ali Cheaito Mar 10 '14 at 17:34
  • if the type of `fileToUpload` is `java.io.File`, then why it is causing error on `size()`. – JPG Jan 05 '17 at 10:54
  • if `fileToUpload` is a `java.io.File`, then, it should be length() instead of size().. correct me if I am wrong.. – JPG Jan 05 '17 at 11:05
2

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;
    }
}
NaN
  • 8,596
  • 20
  • 79
  • 153
0
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
   
   public void fileClientPost(){
        HttpClient httpClient = HttpClientBuilder.create().build();
        BasicHttpContext localContext = new BasicHttpContext();
        
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("...", "...");

        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        HttpEntity entity = (HttpEntity) builder.addPart("file", new FileBody(new File("image.jpg")));
        httpPost.setEntity(entity);

        HttpResponse response = httpClient.execute(httpPost, localContext);
    }