需求:文件流从当前项目转发到另外一个项目。
思路:需要拦截请求中的文件流,组装之后转发到另外一个项目中。
效果:
具体步骤如下:
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