跨項目文件流轉發功能實現
需求:文件流從當前項目轉發到另外一個項目。
思路:需要攔截請求中的文件流,組裝之後轉發到另外一個項目中。
效果:

具體步驟如下:
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 [email protected] * @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 [email protected] * @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 [email protected] * @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 = "//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 [email protected] * @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和攔截器的區別(鏈接: //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
===============================================