Upload a file through an HTTP form, via MultipartEntityBuilder, with a progress bar

后端 未结 3 1481
被撕碎了的回忆
被撕碎了的回忆 2020-11-27 09:53

The short version - org.apache...MultipartEntity is deprecated, and its upgrade, MultipartEntityBuilder, appears under-represented

相关标签:
3条回答
  • 2020-11-27 10:51

    The winning code (in spectacular Java-Heresy(tm) style) is:

    public static String postFile(String fileName, String userName, String password, String macAddress) throws Exception {
    
        HttpClient client = new DefaultHttpClient();
        HttpPost post = new HttpPost(SERVER + "uploadFile");
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();        
        builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
    
        final File file = new File(fileName);
        FileBody fb = new FileBody(file);
    
        builder.addPart("file", fb);  
        builder.addTextBody("userName", userName);
        builder.addTextBody("password", password);
        builder.addTextBody("macAddress",  macAddress);
        final HttpEntity yourEntity = builder.build();
    
        class ProgressiveEntity implements HttpEntity {
            @Override
            public void consumeContent() throws IOException {
                yourEntity.consumeContent();                
            }
            @Override
            public InputStream getContent() throws IOException,
                    IllegalStateException {
                return yourEntity.getContent();
            }
            @Override
            public Header getContentEncoding() {             
                return yourEntity.getContentEncoding();
            }
            @Override
            public long getContentLength() {
                return yourEntity.getContentLength();
            }
            @Override
            public Header getContentType() {
                return yourEntity.getContentType();
            }
            @Override
            public boolean isChunked() {             
                return yourEntity.isChunked();
            }
            @Override
            public boolean isRepeatable() {
                return yourEntity.isRepeatable();
            }
            @Override
            public boolean isStreaming() {             
                return yourEntity.isStreaming();
            } // CONSIDER put a _real_ delegator into here!
    
            @Override
            public void writeTo(OutputStream outstream) throws IOException {
    
                class ProxyOutputStream extends FilterOutputStream {
                    /**
                     * @author Stephen Colebourne
                     */
    
                    public ProxyOutputStream(OutputStream proxy) {
                        super(proxy);    
                    }
                    public void write(int idx) throws IOException {
                        out.write(idx);
                    }
                    public void write(byte[] bts) throws IOException {
                        out.write(bts);
                    }
                    public void write(byte[] bts, int st, int end) throws IOException {
                        out.write(bts, st, end);
                    }
                    public void flush() throws IOException {
                        out.flush();
                    }
                    public void close() throws IOException {
                        out.close();
                    }
                } // CONSIDER import this class (and risk more Jar File Hell)
    
                class ProgressiveOutputStream extends ProxyOutputStream {
                    public ProgressiveOutputStream(OutputStream proxy) {
                        super(proxy);
                    }
                    public void write(byte[] bts, int st, int end) throws IOException {
    
                        // FIXME  Put your progress bar stuff here!
    
                        out.write(bts, st, end);
                    }
                }
    
                yourEntity.writeTo(new ProgressiveOutputStream(outstream));
            }
    
        };
        ProgressiveEntity myEntity = new ProgressiveEntity();
    
        post.setEntity(myEntity);
        HttpResponse response = client.execute(post);        
    
        return getContent(response);
    
    } 
    
    public static String getContent(HttpResponse response) throws IOException {
        BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
        String body = "";
        String content = "";
    
        while ((body = rd.readLine()) != null) 
        {
            content += body + "\n";
        }
        return content.trim();
    }
    
    #  NOTE ADDED LATER: as this blasterpiece gets copied into various code lineages, 
    #  The management reminds the peanut gallery that "Java-Heresy" crack was there
    #  for a reason, and (as commented) most of that stuff can be farmed out to off-
    #  the-shelf jar files and what-not. That's for the java lifers to tool up. This
    #  pristine hack shall remain obviousized for education, and for use in a pinch.
    
    #  What are the odds??
    
    0 讨论(0)
  • 2020-11-27 10:55

    first of all: huge thanks for the original question/answer. Since HttpPost is now deprecated, I reworked it a bit though, using additional input from this article and made a micro library of it: https://github.com/licryle/HTTPPoster

    It wraps the whole in an ASync task; uses the MultipartEntityBuilder & HttpURLConnection and let's you listen for callbacks.

    To use:

    1. Download & extract
    2. In your build.gradle Module file, add the dependency:
    dependencies 
    {    
         compile project(':libs:HTTPPoster') 
    }
    
    1. You need a class to implement the HttpListener interface so you can listen to the callbacks. It has four callbacks in HTTPListener:

      • onStartTransfer
      • onProgress
      • onFailure
      • onResponse
    2. Configure the ASyncTask & start it. Here's a quick usage:

    HashMap<String, String> mArgs = new HashMap<>();
    mArgs.put("lat", "40.712784");
    mArgs.put("lon", "-74.005941");
    
    ArrayList<File> aFileList = getMyImageFiles();
    
    HttpConfiguration mConf = new HttpConfiguration(
        "http://example.org/HttpPostEndPoint",
        mArgs,
        aFileList,
        this, // If this class implements HttpListener
        null,  // Boundary for Entities - Optional
        15000  // Timeout in ms for the connection operation
        10000, // Timeout in ms for the reading operation
    );
    
    new HttpPoster().execute(mConf);
    

    hope that can help :) Feel also free to suggest improvements! It's very recent, and I extend it as I need it.

    Cheers

    0 讨论(0)
  • 2020-11-27 10:57

    Cant thank Phlip enough for that solution. Here are the final touches for adding your progressbar support. I ran it inside an AsyncTask - progress below enables you to post the progress back to a method in the AsyncTask that invokes AsyncTask.publishProgress() for your class running in the AsyncTask. The progress bar isn't exactly smooth but at least it moves. On a Samsung S4 uploading a 4MB imagefile after the preamble it was moving 4K chunks.

         class ProgressiveOutputStream extends ProxyOutputStream {
                long totalSent;
                public ProgressiveOutputStream(OutputStream proxy) {
                       super(proxy);
                       totalSent = 0;
                }
    
                public void write(byte[] bts, int st, int end) throws IOException {
    
                // FIXME  Put your progress bar stuff here!
                // end is the amount being sent this time
                // st is always zero and end=bts.length()
    
                     totalSent += end;
                     progress.publish((int) ((totalSent / (float) totalSize) * 100));
                     out.write(bts, st, end);
                }
    
    0 讨论(0)
提交回复
热议问题