【问题标题】:Upload files from Java client to a HTTP server将文件从 Java 客户端上传到 HTTP 服务器
【发布时间】:2011-01-28 23:50:08
【问题描述】:

我想将一些文件上传到 HTTP 服务器。基本上我需要的是某种带有一些参数和文件的对服务器的 POST 请求。我已经看到了仅上传文件的示例,但没有找到如何传递其他参数。

这样做最简单且免费的解决方案是什么?有人有我可以研究的文件上传示例吗?我已经在谷歌上搜索了几个小时,但是(也许这只是其中的一天)找不到我需要的东西。最好的解决方案是不涉及任何第三方类或库。

【问题讨论】:

    标签: java file-upload urlconnection


    【解决方案1】:

    您通常会使用java.net.URLConnection 来触发 HTTP 请求。您通常还对混合 POST 内容(二进制和字符数据)使用 multipart/form-data 编码。单击链接,它包含信息和如何编写multipart/form-data 请求正文的示例。该规范在RFC2388 中有更详细的描述。

    这是一个启动示例:

    String url = "http://example.com/upload";
    String charset = "UTF-8";
    String param = "value";
    File textFile = new File("/path/to/file.txt");
    File binaryFile = new File("/path/to/file.bin");
    String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
    String CRLF = "\r\n"; // Line separator required by multipart/form-data.
    
    URLConnection connection = new URL(url).openConnection();
    connection.setDoOutput(true);
    connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
    
    try (
        OutputStream output = connection.getOutputStream();
        PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
    ) {
        // Send normal param.
        writer.append("--" + boundary).append(CRLF);
        writer.append("Content-Disposition: form-data; name=\"param\"").append(CRLF);
        writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
        writer.append(CRLF).append(param).append(CRLF).flush();
    
        // Send text file.
        writer.append("--" + boundary).append(CRLF);
        writer.append("Content-Disposition: form-data; name=\"textFile\"; filename=\"" + textFile.getName() + "\"").append(CRLF);
        writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); // Text file itself must be saved in this charset!
        writer.append(CRLF).flush();
        Files.copy(textFile.toPath(), output);
        output.flush(); // Important before continuing with writer!
        writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.
    
        // Send binary file.
        writer.append("--" + boundary).append(CRLF);
        writer.append("Content-Disposition: form-data; name=\"binaryFile\"; filename=\"" + binaryFile.getName() + "\"").append(CRLF);
        writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(binaryFile.getName())).append(CRLF);
        writer.append("Content-Transfer-Encoding: binary").append(CRLF);
        writer.append(CRLF).flush();
        Files.copy(binaryFile.toPath(), output);
        output.flush(); // Important before continuing with writer!
        writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.
    
        // End of multipart/form-data.
        writer.append("--" + boundary + "--").append(CRLF).flush();
    }
    
    // Request is lazily fired whenever you need to obtain information about response.
    int responseCode = ((HttpURLConnection) connection).getResponseCode();
    System.out.println(responseCode); // Should be 200
    

    当您使用 Apache Commons HttpComponents Client 等 3rd 方库时,此代码不那么冗长。

    Apache Commons FileUpload 在这里有些人错误地建议只对服务器端感兴趣。在客户端你不能使用也不需要它。

    另见

    【讨论】:

    • 谢谢,我试试。问题在于这一行: writer.println(paramToSend);我不应该使用编码器来编码这个值吗?还是按原样发送?
    • 不,是multipart/form-data,而不是application/x-www-form-urlencoded。但是,您想指定使用的字符集。我已经相应地更新了答案。
    • 我刚试过。出了点问题 :( 另一端的 PHP 脚本将整个内容视为 post 参数。它认为“Content-type ...等”是参数,其他都是值。我使用的是 linux,这可能是 println 的问题,因为 AFAIK 这些东西应该使用 \r\n 结束线?还是只是缺少一些东西?
    • 没关系,我认为不应将内容类型写入正文并作为标头发送,因此以下行对其进行了修复: connection.setRequestProperty("Content-Type", "multipart/form -data;boundary="+boundary);
    • @Drifter64: 嗯,只需将throws IOException 添加到public void foo() throws IOException 中的方法即可。到目前为止,没有明智的方法来处理发布的代码中的异常,因此调用者有责任这样做。另请参阅 Oracle 关于异常的基本 Java 教程。在这种特殊情况下,肯定需要嵌套的 try 块,因为必须保证在使用后释放外部资源,否则您的应用程序迟早会因为耗尽资源而杀死运行时环境。这绝对不是风格问题。它只是以正确的方式编写代码。
    【解决方案2】:

    以下是使用 Apache HttpClient 的方法(此解决方案适用于不介意使用 3rd 方库的人):

        HttpEntity entity = MultipartEntityBuilder.create()
                           .addPart("file", new FileBody(file))
                           .build();
    
        HttpPost request = new HttpPost(url);
        request.setEntity(entity);
    
        HttpClient client = HttpClientBuilder.create().build();
        HttpResponse response = client.execute(request);
    

    【讨论】:

    • MultipartEntity 已弃用,我们必须改用 MultipartBuilder:HttpEntity entity = MultipartEntityBuilder.create().addTextBody("field1", "value1").addBinaryBody("myfile", new File("/path/file1.txt"), ContentType.create("application/octet-stream"), "file1.txt").build();
    • 您还需要像这样替换已弃用的 DefaultHttpClient:HttpClient client = HttpClientBuilder.create().build();
    • Emmanuel - 我仍然收到 400,来自端点的错误请求。你能查一下stackoverflow.com/questions/70543352/…吗?
    【解决方案3】:

    点击链接获取示例文件上传clint java with apache HttpComponents

    http://hc.apache.org/httpcomponents-client-ga/httpmime/examples/org/apache/http/examples/entity/mime/ClientMultipartFormPost.java

    和图书馆下载链接

    https://hc.apache.org/downloads.cgi

    使用 4.5.3.zip 它在我的代码中运行良好

    还有我的工作代码..

    import java.io.File;
    import org.apache.http.HttpEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.ContentType;
    import org.apache.http.entity.mime.MultipartEntityBuilder;
    import org.apache.http.entity.mime.content.FileBody;
    import org.apache.http.entity.mime.content.StringBody;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    
    public class ClientMultipartFormPost {
    
         public static void main(String[] args) throws Exception {
    
              CloseableHttpClient httpclient = HttpClients.createDefault();
              try {
                 HttpPost httppost = new HttpPost("http://localhost:8080/MyWebSite1/UploadDownloadFileServlet");
    
                 FileBody bin = new FileBody(new File("E:\\meter.jpg"));
                 StringBody comment = new StringBody("A binary file of some kind", ContentType.TEXT_PLAIN);
    
                 HttpEntity reqEntity = MultipartEntityBuilder.create()
                    .addPart("bin", bin)
                    .addPart("comment", comment)
                    .build();
    
    
                 httppost.setEntity(reqEntity);
    
                 System.out.println("executing request " + httppost.getRequestLine());
                 CloseableHttpResponse response = httpclient.execute(httppost);
               try {
                    System.out.println("----------------------------------------");
                    System.out.println(response.getStatusLine());
                    HttpEntity resEntity = response.getEntity();
                    if (resEntity != null) {
                         System.out.println("Response content length: " +    resEntity.getContentLength());
                    }
                  EntityUtils.consume(resEntity);
                 } finally {
                     response.close();
                }
           } finally {
              httpclient.close();
          }
       }
    
    }
    

    【讨论】:

    【解决方案4】:

    以下是使用 Java 11 的 java.net.http 包的方法:

        var fileA = new File("a.pdf");
        var fileB = new File("b.pdf");
    
        var mimeMultipartData = MimeMultipartData.newBuilder()
                .withCharset(StandardCharsets.UTF_8)
                .addFile("file1", fileA.toPath(), Files.probeContentType(fileA.toPath()))
                .addFile("file2", fileB.toPath(), Files.probeContentType(fileB.toPath()))
                .build();
    
        var request = HttpRequest.newBuilder()
                .header("Content-Type", mimeMultipartData.getContentType())
                .POST(mimeMultipartData.getBodyPublisher())
                .uri(URI.create("http://somehost/upload"))
                .build();
    
        var httpClient = HttpClient.newBuilder().build();
        var response = httpClient.send(request, BodyHandlers.ofString());
    

    使用以下 MimeMultipartData:

    public class MimeMultipartData {
    
        public static class Builder {
    
            private String boundary;
            private Charset charset = StandardCharsets.UTF_8;
            private List<MimedFile> files = new ArrayList<MimedFile>();
            private Map<String, String> texts = new LinkedHashMap<>();
    
            private Builder() {
                this.boundary = new BigInteger(128, new Random()).toString();
            }
    
            public Builder withCharset(Charset charset) {
                this.charset = charset;
                return this;
            }
    
            public Builder withBoundary(String boundary) {
                this.boundary = boundary;
                return this;
            }
    
            public Builder addFile(String name, Path path, String mimeType) {
                this.files.add(new MimedFile(name, path, mimeType));
                return this;
            }
    
            public Builder addText(String name, String text) {
                texts.put(name, text);
                return this;
            }
    
            public MimeMultipartData build() throws IOException {
                MimeMultipartData mimeMultipartData = new MimeMultipartData();
                mimeMultipartData.boundary = boundary;
    
                var newline = "\r\n".getBytes(charset);
                var byteArrayOutputStream = new ByteArrayOutputStream();
                for (var f : files) {
                    byteArrayOutputStream.write(("--" + boundary).getBytes(charset)); 
                    byteArrayOutputStream.write(newline);
                    byteArrayOutputStream.write(("Content-Disposition: form-data; name=\"" + f.name + "\"; filename=\"" + f.path.getFileName() + "\"").getBytes(charset));
                    byteArrayOutputStream.write(newline);
                    byteArrayOutputStream.write(("Content-Type: " + f.mimeType).getBytes(charset));
                    byteArrayOutputStream.write(newline);
                    byteArrayOutputStream.write(newline);
                    byteArrayOutputStream.write(Files.readAllBytes(f.path));
                    byteArrayOutputStream.write(newline);
                }
                for (var entry: texts.entrySet()) {
                    byteArrayOutputStream.write(("--" + boundary).getBytes(charset));
                    byteArrayOutputStream.write(newline);
                    byteArrayOutputStream.write(("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"").getBytes(charset));
                    byteArrayOutputStream.write(newline);
                    byteArrayOutputStream.write(newline);
                    byteArrayOutputStream.write(entry.getValue().getBytes(charset));
                    byteArrayOutputStream.write(newline);
                }
                byteArrayOutputStream.write(("--" + boundary + "--").getBytes(charset));
    
                mimeMultipartData.bodyPublisher = BodyPublishers.ofByteArray(byteArrayOutputStream.toByteArray());
                return mimeMultipartData;
            }
    
            public class MimedFile {
    
                public final String name;
                public final Path path;
                public final String mimeType;
    
                public MimedFile(String name, Path path, String mimeType) {
                    this.name = name;
                    this.path = path;
                    this.mimeType = mimeType;
                }
            }
        }
    
        private String boundary;
        private BodyPublisher bodyPublisher;
    
        private MimeMultipartData() {
        }
    
        public static Builder newBuilder() {
            return new Builder();
        }
    
        public BodyPublisher getBodyPublisher() throws IOException {
            return bodyPublisher;
        }
    
        public String getContentType() {
            return "multipart/form-data; boundary=" + boundary;
        }
    
    }
    

    【讨论】:

      【解决方案5】:
      public static String simSearchByImgURL(int  catid ,String imgurl) throws IOException{
          CloseableHttpClient httpClient = HttpClients.createDefault();
          CloseableHttpResponse response = null;
          String result =null;
          try {
              HttpPost httppost = new HttpPost("http://api0.visualsearchapi.com:8084/vsearchtech/api/v1.0/apisim_search");
              StringBody catidBody = new StringBody(catid+"" , ContentType.TEXT_PLAIN);
              StringBody keyBody = new StringBody(APPKEY , ContentType.TEXT_PLAIN);
              StringBody langBody = new StringBody(LANG , ContentType.TEXT_PLAIN);
              StringBody fmtBody = new StringBody(FMT , ContentType.TEXT_PLAIN);
              StringBody imgurlBody = new StringBody(imgurl , ContentType.TEXT_PLAIN);
              MultipartEntityBuilder builder = MultipartEntityBuilder.create();
              builder.addPart("apikey", keyBody).addPart("catid", catidBody)
              .addPart("lang", langBody)
              .addPart("fmt", fmtBody)
              .addPart("imgurl", imgurlBody);
              HttpEntity reqEntity =  builder.build();
              httppost.setEntity(reqEntity);
              response = httpClient.execute(httppost);
              HttpEntity resEntity = response.getEntity();
              if (resEntity != null) {
                 // result = ConvertStreamToString(resEntity.getContent(), "UTF-8");
                  String charset = "UTF-8";   
                String content=EntityUtils.toString(response.getEntity(), charset);   
                  System.out.println(content);
              }
              EntityUtils.consume(resEntity);
          }catch(Exception e){
              e.printStackTrace();
          }finally {
              response.close();
              httpClient.close();
          }
          return result;
      }
      

      【讨论】:

      【解决方案6】:
      protected void doPost(HttpServletRequest request,
              HttpServletResponse response) throws ServletException, IOException {
      
          boolean isMultipart = ServletFileUpload.isMultipartContent(request);
      
          if (!isMultipart) {
              return;
          }
      
          DiskFileItemFactory factory = new DiskFileItemFactory();
      
          factory.setSizeThreshold(MAX_MEMORY_SIZE);
      
          factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
      
          String uploadFolder = getServletContext().getRealPath("")
                  + File.separator + DATA_DIRECTORY;//DATA_DIRECTORY is directory where you upload this file on the server
      
          ServletFileUpload upload = new ServletFileUpload(factory);
      
          upload.setSizeMax(MAX_REQUEST_SIZE);//MAX_REQUEST_SIZE is the size which size you prefer
      

      在html中使用&lt;form enctype="multipart/form-data"&gt;&lt;input type="file"&gt;

      【讨论】:

      • 那是服务器端(servlet)。 OP 要求 Java 客户端。
      • 没有理由仅仅因为它没有直接回答 OP 的问题而投反对票。大多数来到这里的人都来自谷歌搜索结果,他们会发现这样的答案很有帮助。
      【解决方案7】:

      这可能取决于您的框架。 (因为它们中的每一个都可能存在一个更简单的解决方案)。

      但要回答您的问题:此功能有 a lot of external libraries。看here如何使用apache commons fileupload。

      【讨论】:

      • 我没有投反对票,但我想这与其他答案被否决的原因相同。您回答如何使用 servlet 接收上传的文件,问题是询问如何发送文件。具体来说,在 POST 中包含其他数据。一般来说,如果您从链接到的页面中解释或复制有用/相关部分,将使答案对读者更有用,并在链接失效时保留有用性。
      猜你喜欢
      • 2014-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多