1.跨项目文件流转发功能实现
需求:文件流从当前项目转发到另外一个项目。
思路:需要拦截请求中的文件流,组装之后转发到另外一个项目中。
效果:

具体步骤如下:
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.cnblogs.com/natian-ws/p/10824363.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
===============================================
参考资料:
https://www.cnblogs.com/yskcoder/p/4718198.html
https://www.cnblogs.com/natian-ws/p/10824363.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用