需求:文件流从当前项目转发到另外一个项目。
思路:需要拦截请求中的文件流,组装之后转发到另外一个项目中。
效果:
具体步骤如下:
1.首先读取文件的接口
api接口,接收文件:
- package com.zxy.demo.api;
-
- import com.zxy.demo.service.FileService;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.*;
- import org.springframework.web.multipart.MultipartFile;
-
- /**
- * FileUpload
- *
- * @author zhouxy@133.cn
- * @date 2022/3/1
- **/
- @RestController
- @RequestMapping("/file")
- @Slf4j
- public class FileUploadController {
- @Autowired
- private FileService fileService;
-
- @PostMapping("/upload")
- public String test(@RequestParam("file") MultipartFile file) {
- fileService.uploadFile(file);
- return "成功";
- }
- }
-
文件处理逻辑,读取文件内容:
- package com.zxy.demo.service.impl;
-
- import com.zxy.demo.service.FileService;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.stereotype.Service;
- import org.springframework.web.multipart.MultipartFile;
-
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
-
- /**
- * FileServiceImpl
- *
- * @author zhouxy@133.cn
- * @date 2022/3/1
- **/
- @Slf4j
- @Service
- public class FileServiceImpl implements FileService {
- @Override
- public void uploadFile(MultipartFile file) {
- try (InputStream is = file.getInputStream();
- InputStreamReader isReader = new InputStreamReader(is);
- BufferedReader br = new BufferedReader(isReader);) {
- log.info("文件打印:");
- while (br.ready()) {
- log.info("{}", br.readLine());
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- }
- }
-
2.调用方代码,拦截器处理文件流
- package com.zxy.demo.config;
-
- import lombok.extern.slf4j.Slf4j;
- import org.apache.commons.fileupload.FileItem;
- import org.apache.commons.fileupload.FileItemFactory;
- import org.apache.commons.fileupload.FileUploadException;
- import org.apache.commons.fileupload.disk.DiskFileItemFactory;
- import org.apache.commons.fileupload.servlet.ServletFileUpload;
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpResponse;
- import org.apache.http.client.config.RequestConfig;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.entity.ContentType;
- import org.apache.http.entity.InputStreamEntity;
- import org.apache.http.entity.mime.HttpMultipartMode;
- import org.apache.http.entity.mime.MultipartEntityBuilder;
- import org.apache.http.impl.client.CloseableHttpClient;
- import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
- import org.apache.http.impl.client.HttpClients;
- import org.apache.http.protocol.HttpDateGenerator;
- import org.apache.http.util.EntityUtils;
- import org.springframework.web.servlet.HandlerInterceptor;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.DataInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.nio.charset.Charset;
- import java.util.List;
-
- /**
- * InterceptorConfig
- *
- * @author zhouxy@133.cn
- * @date 2022/3/3
- **/
- @Slf4j
- public class InterceptorConfig implements HandlerInterceptor {
-
- private static final CloseableHttpClient httpclient;
-
- static {
- //创建http客户端
- httpclient = HttpClients.custom()
- .useSystemProperties()
- .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true))
- .build();
- }
-
-
- /**
- * 进入controller层之前拦截请求
- *
- * @param request
- * @param httpServletResponse
- * @param o
- * @return
- * @throws Exception
- */
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse httpServletResponse, Object o) throws Exception {
- log.info("");
- String url = request.getRequestURI();
- String newUrl = "http://localhost:8081/file/upload";
- if (url.contains("/file/upload")) {
- String result = null;
- /*1.第一种读取流方式*/
- MultipartEntityBuilder builder = readFile(request);
- result = postResultMultipartFile(newUrl, builder, "文件上传");
-
- /*2.第二种读取流方式*/
- /*InputStreamEntity inputStreamEntity = new InputStreamEntity(request.getInputStream(), request.getContentLength(), ContentType.parse(request.getContentType()));
- result = postResultMultipartFile(newUrl, inputStreamEntity, "文件上传");*/
- log.info("result====>{}", result);
- return false;
- }
- return true;
- }
-
- /**
- * post请求接口
- *
- * @Author zhouxy
- */
- public String postResultMultipartFile(String url, HttpEntity httpEntity, String actionDesc) {
- HttpEntity responseEntity;
- try {
- HttpPost httpPost = new HttpPost(url);
- httpPost.setEntity(httpEntity);
- RequestConfig config = RequestConfig.custom().setConnectTimeout(3000).build();
- httpPost.setConfig(config);
- log.info("lenght:{}", httpEntity.getContentLength());
- HttpResponse response = httpclient.execute(httpPost);// 执行提交
- responseEntity = response.getEntity();
- if (responseEntity != null) {
- log.info("responseEntity:{}", responseEntity);
- // 将响应内容转换为字符串
- return EntityUtils.toString(responseEntity, Charset.forName("UTF-8"));
- }
- } catch (IOException e) {
- log.error(actionDesc + ":IOException:", e.getMessage());
- } catch (Exception e) {
- log.error(actionDesc + ":Exception:", e.getMessage());
- }
- return null;
- }
-
- /**
- * 读取文件
- *
- * @param request
- * @return
- */
- private MultipartEntityBuilder readFile(HttpServletRequest request) {
- MultipartEntityBuilder builder = MultipartEntityBuilder.create();
- builder.setCharset(Charset.forName("utf-8"));
- builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);//加上此行代码解决返回中文乱码问题
-
- FileItemFactory factory = new DiskFileItemFactory();
- ServletFileUpload upload = new ServletFileUpload(factory);
- upload.setHeaderEncoding("UTF-8");
- List<FileItem> list = null;
- try {
-
- list = upload.parseRequest(request);
- } catch (FileUploadException e) {
- e.printStackTrace();
- }
- log.info("list:{}", list.size());
- for (FileItem file : list) {
- //获取文件名(带后缀名)
- String fileName = file.getName();
- //获取文件输入流
- try (InputStream input = file.getInputStream()) {
- builder.addBinaryBody("file", input, ContentType.MULTIPART_FORM_DATA, fileName);// 文件流
- } catch (IOException e) {
- log.error("文件流读取失败:", e);
- }
- }
- return builder;
- }
-
- /**
- * post请求接口
- *
- * @Author zhouxy
- */
- public String postResultMultipartFile(String url, MultipartEntityBuilder builder, String actionDesc) {
- HttpEntity httpEntity;
- HttpEntity responseEntity;
- try {
- HttpPost httpPost = new HttpPost(url);
- // 添加分销接口默认header,分销新网关凭证
- httpEntity = builder.build();
- httpPost.setEntity(httpEntity);
-
- HttpResponse response = httpclient.execute(httpPost);// 执行提交
- log.info("转发成功:");
- responseEntity = response.getEntity();
- if (responseEntity != null) {
- // 将响应内容转换为字符串
- return EntityUtils.toString(responseEntity, Charset.forName("UTF-8"));
- }
- } catch (IOException e) {
- log.error(actionDesc + ":IOException:", e.getMessage());
- } catch (Exception e) {
- log.error(actionDesc + ":Exception:", e.getMessage());
- }
- return null;
- }
- }
-
调用方文件上传接口:
- package com.zxy.demo.api;
-
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.web.bind.annotation.PostMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.RestController;
- import org.springframework.web.multipart.MultipartFile;
-
- /**
- * FileUpload
- *
- * @author zhouxy@133.cn
- * @date 2022/3/1
- **/
- @RestController
- @RequestMapping("/file")
- @Slf4j
- public class FileUploadController {
-
- @PostMapping("/upload")
- public void test(@RequestParam("file") MultipartFile file) {
- log.info("1111111");
- }
- }
-
完成上述代码之后,测试了一下,不行。发现调用upload.parseRequest(request),返回的list的size为0,不能从request中读取文件流。该问题需要修改一下配置文件application.yml(Springboot项目):
- spring:
- servlet:
- multipart:
- # 禁用multipart读取文件
- enabled: false
-
遇到的问题:
1.读取upload.parseRequest(request)返回size为0的问题,上面已经说了解决办法。
2.刚开始采用的是aop方式,但是获取不到文件。在解决1的问题之后,继续获取,仍然不行。
问题点在于两点:
(1)没分清aop和拦截器的区别(链接: https://www.cdsy.xyz/computer/programme/suggest/230201/cd39995.html),区别如下:
- 过滤器
- 过滤器可以拦截到方法的请求和响应(ServletRequest request, SetvletResponse response),并对请求响应做出响应的过滤操作,比如设置字符编码、鉴权操作。
-
- 拦截器
- 拦截器可以在方法之前(preHandle)和方法执行之后(afterCompletion)进行操作,回调操作(postHandle),可以获取执行的方法的名称,请求(HttpServletRequest)。
-
- AOP切片
- AOP操作可以对操作进行横向的拦截,最大的优势在于可以获取执行方法的参数,对方法进行统一的处理,常见使用日志,事务,请求参数安全验证等
-
(2)MultipartFile的读取流程
MultipartFile读取文件流,是从HttpServletRequest中读取文件流,而request中的流只能读取一次。因此aop做切片,无法从中读取文件流。
使用aop读取文件流会报以下错误。
- 严重: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.lang.IllegalStateException: Unable to process parts as no multi-part configuration has been provided] with root cause
- java.lang.IllegalStateException: Unable to process parts as no multi-part configuration has been provided
-