跨項目文件流轉發功能實現

需求:文件流從當前項目轉發到另外一個項目。
思路:需要攔截請求中的文件流,組裝之後轉發到另外一個項目中。
效果:
具體步驟如下:
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

===============================================

參考資料://www.cnblogs.com/yskcoder/p/4718198.html

 
Tags: