POST请求实现文件传输 | 您所在的位置:网站首页 › mydownloads文件 › POST请求实现文件传输 |
通过POST请求以form-data形式在前后端以及后端服务间传递文件。 通过设置http请求response的content-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 multiFileMap = standardMultipartRequest.getMultiFileMap(); for (String paramKey : multiFileMap.keySet()) { List 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 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调试接口设置请求body为form-data类型,可以同时传递文本数据和文件数据,文件key允许包含多个文件(file_excel),key也允许重复(id, name, email)。 从运行结果来看,@RequestParam注解可以获取form-data中指定key的数据,而HttpServletRequest request则包含了所有的form-data数据。 由于@RequestParam修饰的email和multipartFile不是数组或者List类型,文本类型的email取拼接值,而文件类型的multipartFile则取第1个value值。修改接口定义,改为List multipartFile就可以接收多个文件。 以简单的html演示在页面填写数据,点击提交时调用1.1节定义的接口并把数据以form-data形式传递给后端。 Upload FormData ID: ID: Name: Name: Email: Email: File: File: 复制代码展示如何后端直接封装form-data格式的数据,然后调用1.1节定义的接口,实现后端服务间的文件数据传递。 通过HttpHeaders设置请求传递的数据类型为form-data,通过HttpEntity封装requset header和request body,通过RestTemplate的postForObject方法调用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 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", "[email protected]"); requestBody.add("email", "[email protected]"); 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 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-data的key不为参数名multipartFile时,变量multipartFile无法初始化,为null。只有当form-data的key设置为multipartFile时,才能正常初始化。 如果修改接口定义为 public String receiveSingle(@RequestParam(value = "file") MultipartFile multipartFile) throws IOException 复制代码那么form-data的key只能设置为file才能完成multipartFile变量的初始化。因此建议@RequestParam中value值和变量名保存一致,接口调用时封装参数的key也用变量名。 1.6 总结 以form-data传输数据给后端,需要设置request header中content-type="multipart/form-data"。 form-data可以同时传递文本和文件给后端。 对于文件类型的form-data数据,后端接口以MultiPartFile类型接收,并可以通过MultiPartFile的transferTo将文件保存到本地。 对于文本类型的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接收并展示 Title base64 image 获取并展示图片 byte image 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!"); } }); } 复制代码运行效果: HTTP 消息头允许客户端和服务器通过 request和 response传递附加信息。一个请求头由名称(不区分大小写)后跟一个冒号“:”,冒号后跟具体的值(不带换行符)组成。 根据不同上下文,可将消息头分为: 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的长度参考资料: HTTP Headers List of HTTP header fields |
CopyRight 2018-2019 实验室设备网 版权所有 |