欢迎您的访问
专注架构,Java,数据结构算法,Python技术分享

HttpClient模拟文件上传

  • 本例使用的是springboot,具体的springboot依赖就不贴了,只贴httpclient请求需要的依赖.需要注意的是,httpcore的版本和httpclient的版本并不一致.
 <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.12</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
            <version>4.4.13</version>
        </dependency>

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpmime</artifactId>
            <version>4.5.12</version>
        </dependency>
  • 在Java代码中模拟文件服务器端
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

@RestController
@RequestMapping("/file")
public class FileUploadController {
    private String path ="D://data//";
    @RequestMapping(value = "/upload",method = RequestMethod.POST)
    public Object upload(@RequestParam("file") MultipartFile file){
        if(!file.isEmpty()){
            String fileName = file.getOriginalFilename();
            File dest = new File(path + fileName);
            try {
                file.transferTo(dest);
                return "上传成功";
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        return "上传失败!";
    }
}
  • 启动工程,通过Postman工具测试,本例使用的Chrome浏览器插件Talend API Free Edition.效果截图

70_1.png

  • 测试成功,开始通过HttpClient模拟文件上传,本例通过junit测试类进行验证
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;
import org.junit.Test;

import java.io.File;
import java.io.IOException;

public class FileUploadTest {

    @Test
    public void test01() throws IOException {
        CloseableHttpClient httpClient = HttpClients.createDefault();

        HttpPost httpPost = new HttpPost("http://localhost:8080/file/upload");
        httpPost.setHeader("Content-type", ContentType.MULTIPART_FORM_DATA.getMimeType());

        File file = new File("D://data.docx");

        FileBody bin = new FileBody(file);
        StringBody comment = new StringBody(file.getName(), ContentType.TEXT_PLAIN);
        HttpEntity reqEntity = MultipartEntityBuilder.create().addPart("file", bin).build();
        httpPost.setEntity(reqEntity);
        System.out.println("executing request " + httpPost.getRequestLine());
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            System.out.println(response.getStatusLine());
            HttpEntity resEntity = response.getEntity();
            if (resEntity != null) {
                String responseEntityStr = EntityUtils.toString(response.getEntity());
                System.out.println(responseEntityStr);
            }
            EntityUtils.consume(resEntity);
        } finally {
            response.close();
        }

    }
}
  • 美滋滋的写完,开始测试,结果报错了
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
  • 网上搜下问题,找到了如下内容

stackoverflow.com/questions/1…

You should NEVER set that header yourself. We set the header properly with the boundary. If you set that header, we won't and your server won't know what boundary to expect (since it is added to the header). Remove your custom Content-Type header and you'll be fine

大体的意思就是去掉header中的Content-type,它会自动给加上;我在测试类中去掉header关于Content-type的配置后,发现果然可以了.

  • 那么问题来了,为什么加上去就不可以了呢.我大概翻了几个网上的文章,好像是和boundary这个属性有关,因为在网页请求的时候,会在Content-type:multipart/form-data 后面跟上boundary一串内容.
boundary 用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂。然后 Content-Type 里指明了数据是以 mutipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary 开始,紧接着内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。
如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary-- 标示结束。这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。

 

赞(0) 打赏
版权归原创作者所有,任何形式转载请联系作者;码农code之路 » HttpClient模拟文件上传

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏