阅读 1285

POST请求实现文件传输(浏览器怎么发送post请求)

通过POST请求以form-data形式在前后端以及后端服务间传递文件。

通过设置http请求responsecontent-type,后端以二进制流传递数据给前端。

1 以form-data传数据给后端

1.1 后端数据接收接口定义

后端接口/form-data定义

@Slf4j @RestController @RequestMapping(value = "/rest") public class FileTransmit {     @PostMapping(value = "/form-data")     public String formData(HttpServletRequest request,                            @RequestParam(value = "email", required = false) String email,                            @RequestParam(value = "file_excel", required = false) MultipartFile multipartFile,                            UserInfo userInfo) {         StandardMultipartHttpServletRequest standardMultipartRequest = (StandardMultipartHttpServletRequest) request;          log.info("multipartFile in @RequestParam={}", multipartFile.getOriginalFilename());         MultiValueMap<String, MultipartFile> multiFileMap = standardMultipartRequest.getMultiFileMap();         for (String paramKey : multiFileMap.keySet()) {             List<MultipartFile> multipartFileList = multiFileMap.get(paramKey);             log.info("MultipartFile key={}, size={}", paramKey, multipartFileList.size());             for (MultipartFile curMultipartFile : multipartFileList) {                 log.info("MultipartFile key={}, file name={}",                         paramKey, curMultipartFile.getOriginalFilename());                 log.info("MultipartFile in request equals to file in @RequestParam={}",                         multipartFile.equals(curMultipartFile));             }         }          log.info("email in @RequestParam={}", email);         log.info("userInfo in RequestParam={}", userInfo);         Map<String, String[]> paramMap = standardMultipartRequest.getParameterMap();         for (String paramKey : paramMap.keySet()) {             log.info("param key={}, size={}, value={}",                     paramKey, paramMap.get(paramKey).length, paramMap.get(paramKey));         }         return "Form Data processed finished!";     } } 复制代码

1.2 postman调试接口

image-20220103112627238.png

设置请求body为form-data类型,可以同时传递文本数据和文件数据,文件key允许包含多个文件(file_excel),key也允许重复(id, name, email)。

从运行结果来看,@RequestParam注解可以获取form-data中指定key的数据,而HttpServletRequest request则包含了所有的form-data数据。

由于@RequestParam修饰的emailmultipartFile不是数组或者List类型,文本类型的email取拼接值,而文件类型的multipartFile则取第1个value值。修改接口定义,改为List<MultipartFile> multipartFile就可以接收多个文件。

image-20220103113051282.png

image-20220103113405153.png

1.3 前端传递form-data

以简单的html演示在页面填写数据,点击提交时调用1.1节定义的接口并把数据以form-data形式传递给后端。

<!DOCTYPE html> <html> <head>     <meta charset="UTF-8">     <title>Upload FormData</title> </head> <body>     <form id="Form1" action="http://localhost:8080/rest/form-data" method="post" enctype="multipart/form-data">         <div style="text-align: left">             ID: <input name="id" type="text" /> <br />             ID: <input name="id" type="text" /> <br />             Name: <input name="name" type="text" /> <br />             Name: <input name="name" type="text" /> <br />             Email: <input name="email" type="text" /> <br />             Email: <input name="email" type="text" /> <br />             File: <input id="1" name="file_excel" type="file" multiple="multiple" /> <br />             File: <input id="2" name="file_img" type="file" /> <br />             <input type="submit" value="submit" />             <input type="reset" value="reset" />         </div>     </form> </body> </html> 复制代码

image-20220103114333475.png

1.4 后端传递form-data

展示如何后端直接封装form-data格式的数据,然后调用1.1节定义的接口,实现后端服务间的文件数据传递。

通过HttpHeaders设置请求传递的数据类型为form-data,通过HttpEntity封装requset headerrequest body,通过RestTemplatepostForObject方法调用POST接口。

使用MultiValueMap封装form-data格式的数据,文件类型数据采用FileSystemResource

public class FileTransmitTest {     private RestTemplate restTemplate;     private final String URL_ROOT = "http://localhost:8080/rest";      @Before     public void init() {         restTemplate = new RestTemplate();     }      @Test     public void FormDataTest() {         MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap();         requestBody.add("file_excel",                          new FileSystemResource(new File("E:\dataJava\data\city_info.xlsx")));         requestBody.add("file_excel",                          new FileSystemResource(new File("E:\dataJava\data\user_info.xlsx")));         requestBody.add("file_img",                          new FileSystemResource(new File("E:\dataJava\data\github.png")));         requestBody.add("email", "town@163.com");         requestBody.add("email", "harden@163.com");         requestBody.add("id", "123");         requestBody.add("id", "12345");         requestBody.add("name", "zhangsan");         requestBody.add("name", "kuangtu");          HttpHeaders httpHeaders = new HttpHeaders();         httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);          HttpEntity<MultiValueMap<String, Object>> requestEntity =              new HttpEntity<>(requestBody, httpHeaders);         String response = restTemplate.postForObject(URL_ROOT+"/form-data", requestEntity, String.class);         System.out.println(response);     } } 复制代码

1.5 传单个文件给后端

演示后端接收单个文件并保存到指定目录。

接口定义

    @ResponseBody     @PostMapping(value = "/receive/single")     public String receiveSingle(MultipartFile multipartFile) throws IOException {         if (multipartFile == null) {             return "received file is null!";         }         log.info("文件content-type={}", multipartFile.getContentType());         log.info("文件大小={}", multipartFile.getSize());         log.info("文件名={}", multipartFile.getName());         log.info("文件原始名={}", multipartFile.getOriginalFilename());         // 保存接收的文件到本地         File destFile = new File("E:\Download\"+multipartFile.getOriginalFilename());         multipartFile.transferTo(destFile);          return "Form Data processed finished!";     } 复制代码

接口调用

由于接口定义中没有使用@RequestParam注解,当form-datakey不为参数名multipartFile时,变量multipartFile无法初始化,为null。只有当form-datakey设置为multipartFile时,才能正常初始化。

image-20220103120539954.png

如果修改接口定义为

public String receiveSingle(@RequestParam(value = "file") MultipartFile multipartFile) throws IOException 复制代码

那么form-datakey只能设置为file才能完成multipartFile变量的初始化。因此建议@RequestParamvalue值和变量名保存一致,接口调用时封装参数的key也用变量名。

1.6 总结

  • form-data传输数据给后端,需要设置request headercontent-type="multipart/form-data"

  • form-data可以同时传递文本和文件给后端。

  • 对于文件类型的form-data数据,后端接口以MultiPartFile类型接收,并可以通过MultiPartFiletransferTo将文件保存到本地。

  • 对于文本类型的form-data数据,可以定义类接收,也可以用基本类型变量逐个接收。

  • Spring工程后端接口中HttpServletRequest request包含了所有的form-data数据。

2 后端传文件给前端

这里以后端传图片给前端使用为例,方法包括以下3种:

  • 后端把图片保存在文件存储服务器上,返回图片的url给前端,前端设置img标签的src=url即可。这种方式应该是主流,但是不想再搞个存储服务器,没有采用这种方式。

  • 后端以把图片的base64编码以字符串的形式返给前端,前端再解析为图片展示。这种方式虽然存在大图片有可能被截断的肯,但是来得方便,选用了这种方式。

  • 后端以把图片的二进制流以字节数组的形式返给前端,前端再解析为图片展示。返回二进制流不仅适用于图片,还适用于音视频。

2.1 以base64编码传输

后端接口定义

    @CrossOrigin  //允许跨域访问     @GetMapping(value = "/get-img-code")     public String getImageBase64(String imageName) throws IOException {         log.info("request param={}", imageName);         String imgRootPath = "E:\dataJava\data\";         File imgFile = new File(imgRootPath + imageName + ".png");         InputStream inStream =new FileInputStream(imgFile);         byte[] imgBytes = new byte[(int) imgFile.length()]; //创建合适文件大小的数组         inStream.read(imgBytes); //读取文件里的内容到b[]数组         inStream.close();         log.info("image size={}", imgBytes.length);         BASE64Encoder encoder = new BASE64Encoder();         return encoder.encodeBuffer(imgBytes);     } 复制代码

2.2 以二进制流传输

后端接口定义

    @CrossOrigin  //允许跨域访问     @GetMapping(value = "/get-img-byte")     public void getImageByte(HttpServletRequest request, HttpServletResponse response, String imageName) throws IOException {         String imgRootPath = "E:\dataJava\data\";         File imgFile = new File(imgRootPath + imageName + ".png");         InputStream inStream =new FileInputStream(imgFile);         byte imgBytes[] = new byte[(int) imgFile.length()]; //创建合适文件大小的数组         inStream.read(imgBytes); //读取文件里的内容到b[]数组         inStream.close();          response.setContentType("application/octet-stream;charSet=UTF-8");         response.setContentLength(imgBytes.length);         try (InputStream inputStream = new ByteArrayInputStream(imgBytes);             OutputStream outputStream = response.getOutputStream()) {             IOUtils.copy(inputStream, outputStream);             outputStream.flush();         }catch (IOException e) {             log.error("{}", e.getMessage());         }     } 复制代码

2.3 html接收并展示

<!DOCTYPE html> <html> <head>     <meta charset="UTF-8">     <title>Title</title>     <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">     </script> </head> <body>     <div id="main" style="width:100%;">         <div id="left" style="width:50%;float:left;">             <p>base64 image</p>             <p><button id="getBase64" onclick="getImgBase64()">获取并展示图片</button> </p>             <img id="base64_img" src="" alt="" width="" height="">         </div>         <div id="right" style="width:50%;float:left;">             <p>byte image</p>             <img id="byte_img" src="http://localhost:8080/rest/get-img-byte?imageName=github" alt="" width="70%" height="70%">         </div>     </div>      <script>         function getImgBase64() {             $.ajax({                 url : "http://localhost:8080/rest/get-img-code?imageName=github",                 type : 'GET',                 contentType : false,  //必须false才会自动加上正确的Content-Type                 success : function(result) {  //jquery请求返回的结果好像都是字符串类型                     console.log("reponse result:", result);                     var src = 'data:image/png;base64,' + result;                     $("#base64_img").attr('src', src);                     $("#base64_img").css("width", "70%");                     $("#base64_img").css("height", "70%");                 },                 error : function(result) {                     console.log("reponse result:", result);                     alert("Post Faile!");                 }             });         }     </script> </body> </html> 复制代码

运行效果:

image-20220103143611952.png

附:HTTP Headers

HTTP 消息头允许客户端和服务器通过 requestresponse传递附加信息。一个请求头由名称(不区分大小写)后跟一个冒号“:”,冒号后跟具体的值(不带换行符)组成。

根据不同上下文,可将消息头分为:

  • General headers: 同时适用于请求和响应消息,但与最终消息主体中传输的数据无关的消息头。

  • Request headers: 包含更多有关要获取的资源或客户端本身信息的消息头。

  • Response headers: 包含有关响应的补充信息,如其位置或服务器本身(名称和版本等)的消息头。

  • Entity headers: 包含有关实体主体的更多信息,比如主体长(Content-Length)度或其MIME类型。

Request headers

  • Accept:表示浏览器告诉服务端,浏览器可以接收的数据类型。

  • Accept-Encoding:表示浏览器告诉服务端,浏览器可以接收的数据的编码方式。

  • Accept-Language:表示浏览器告诉服务端,浏览器可以接收的数据语言,通常用于做国际化。

  • Content-Type:POST和PUT请求body的文件类型。

Reponse headers

  • Content-Type:接口返回的数据类型。

  • Content-Length:octets (8-bit bytes)中返回body的长度


作者:town
链接:https://juejin.cn/post/7048869287716454407


文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐