Spring后臺 RestTemplate post MultipartFile with HttpHeader兩種方案: by FileSystemResource / Resource的自定義實現(xiàn)

原文:
resttemplate multipart post with InputStreamResource not working SPR-13571

方案一:需要將 MultipartFile 從控制器接收后存儲在本地舷礼,然后通過FileSystemResource用RestTemplate發(fā)送鹃彻。
缺點是:多次I/O,使用臨時文件妻献,臨時文件需要清理


方案二:如github上的方案一樣:
我不想將此文件的內(nèi)容存儲在本地(用 FileSystemResource 或 ByteArrayResource )
如果你只使用 ByteArrayResource 將會拋出異常蛛株,所以需要創(chuàng)建一個新類 MultipartFileResource 去繼承 ByteArrayResource 后復(fù)寫 Resource.getFilename()

使用 ByteArrayResource 問題集中在,
1節(jié)數(shù)組只能讀取一次育拨,
2 Spring RestTemplate 為了正確地寫出多參數(shù)請求谨履,用進行 FormHttpMessageConverter 自動配置RestTemplate;
如果部件從Resource繼承熬丧,它將調(diào)用該Resource.getFilename()方法以獲取文件名笋粟,請參見 getFilename()方法。
如果未找到文件名析蝴,則此部分將作為“常規(guī)”部分而不是文件寫入消息的內(nèi)容處置部分害捕。


缺點:這種方式調(diào)用 multipartFile.getBytes()是一個好主意。 那是會將整個文件讀入堆中嫌变,有可能遇到非常大的文件(或者并發(fā)處理的時候)時候OOM

代碼:
client send file to aServer
than aServer deliver the received file to bServer.


import com.arc.core.model.vo.ResponseVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.*;
import org.springframework.stereotype.Controller;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * 轉(zhuǎn)發(fā)MultipartFile 文件測試
 * aServer
 * bServer
 * client send file to aServer than deliver to bServer.
 *
 * @author May
 * @since 2020/1/15 16:48
 */
@Slf4j
@Controller
public class TransferMultipartFileController {


    //接受前段上傳的文件數(shù)據(jù) 轉(zhuǎn)發(fā)給 v1 處理
    @PostMapping("/server/a")
    @ResponseBody
    public ResponseVo prepareFileV0(MultipartFile file) throws IOException {
        long t1 = System.currentTimeMillis();

        // 1 body
        //方法一:重新實現(xiàn) ByteArrayResource 并復(fù)寫   @Override   public String getFilename() {
        ByteArrayResource resource = new MultipartFileResource(file);
        // 方法二:接受到文件流后先暫時持久化到本地臨時文件夾吨艇,然后轉(zhuǎn)發(fā)
        //FileSystemResource resource = new FileSystemResource(new File("文件本地磁盤路徑"));
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        body.add("file", resource);

        //2、 headers
        HttpHeaders headers = new HttpHeaders();

        //設(shè)置 接受的類型
        ArrayList<MediaType> acceptMediaTypes = new ArrayList<>();
        acceptMediaTypes.add(MediaType.APPLICATION_JSON);

        //2.1 設(shè)置header 等效 headers.add("Accept", APPLICATION_JSON.toString());
        headers.setAccept(acceptMediaTypes);

        //2.2 設(shè)置header 是表單提交 等效 headers.setContentType(MediaType.parseMediaType("multipart/form-data;charset=UTF-8"));
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);


        //3腾啥、構(gòu)造請求體 body+header
        HttpEntity<Object> requestEntity = new HttpEntity<>(body, headers);

        //4东涡、發(fā)送請求
        String url = "http://127.0.0.1:8001/zero/test/zip/upload/v1";
        ResponseEntity<Object> responseEntity = new RestTemplate().exchange(url, HttpMethod.POST, requestEntity, Object.class);

        //測試返回數(shù)據(jù)是什么  ResponseEntity
        log.debug("BIO cost time", System.currentTimeMillis() - t1);
        log.debug("結(jié)果={}", responseEntity);
        log.debug("getStatusCodeValue結(jié)果={}", responseEntity.getStatusCodeValue());
        log.debug("getBody結(jié)果={}", responseEntity.getBody());
        log.debug("getHeaders結(jié)果={}", responseEntity.getHeaders());
        return ResponseVo.success(true);
    }


    //========================  模擬 bServer

    //數(shù)據(jù)準備
    @PostMapping("/server/b")
    @ResponseBody
    public ResponseVo prepareFileForDownloadMultiFileInZip(HttpServletRequest request, MultipartFile file) {
        long t1 = System.currentTimeMillis();
        //1、上傳文件,簡單校驗
        if (file == null || file.isEmpty()) {
            return ResponseVo.failure("file is null");
        }

        //2倘待、文件持久化到磁盤
        String path = "T:\\data\\output\\" + file.getOriginalFilename();
        log.debug("文件所在路徑={}" + path);
        boolean result = false;
        try {
            log.debug(" try");
            file.transferTo(new File(path));
            result = true;
        } catch (IOException e) {
            log.debug("執(zhí)行到catch,{}", e);
        } finally {
            log.debug("執(zhí)行到finally");
        }


        //3疮跑、響應(yīng)一個返回數(shù)據(jù)--這里為了測試header 數(shù)據(jù)做了獲取封裝后返回了,(獲取到請求頭數(shù)據(jù)在響應(yīng)回去)
        Map<Object, Object> map = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            String value = request.getHeader(key);
            log.debug("請求頭重的key/value={}/{}", key, value);
            map.put(key, value);
        }

        map.put(result, result);
        map.put("BIO cost time", System.currentTimeMillis() - t1);
        map.put("file size", file.getSize());
        map.put("file name", file.getName());
        map.put("file originalFilename", file.getOriginalFilename());
        map.put("file contentType", file.getContentType());

        return ResponseVo.success(map);
    }
}

import org.springframework.core.io.ByteArrayResource;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

/**
 * 參考:
 * https://github.com/spring-projects/spring-framework/issues/18147
 *
 * @author May
 * @since 2020/1/15 15:32
 */
public class MultipartFileResource extends ByteArrayResource {

    private String filename;

    public MultipartFileResource(MultipartFile multipartFile) throws IOException {
        super(multipartFile.getBytes());
        this.filename = multipartFile.getOriginalFilename();
    }

    @Override
    public String getFilename() {
        return this.filename;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凸舵,一起剝皮案震驚了整個濱河市祖娘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌啊奄,老刑警劉巖渐苏,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異菇夸,居然都是意外死亡琼富,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門庄新,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鞠眉,“玉大人薯鼠,你說我怎么就攤上這事⌒堤#” “怎么了出皇?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長哗戈。 經(jīng)常有香客問我郊艘,道長,這世上最難降的妖魔是什么谱醇? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任暇仲,我火速辦了婚禮,結(jié)果婚禮上副渴,老公的妹妹穿的比我還像新娘。我一直安慰自己全度,他們只是感情好煮剧,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著将鸵,像睡著了一般勉盅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上顶掉,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天草娜,我揣著相機與錄音,去河邊找鬼痒筒。 笑死宰闰,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的簿透。 我是一名探鬼主播移袍,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼老充!你這毒婦竟也來了葡盗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤啡浊,失蹤者是張志新(化名)和其女友劉穎觅够,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巷嚣,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡喘先,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了涂籽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苹祟。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出树枫,到底是詐尸還是另有隱情直焙,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布砂轻,位于F島的核電站奔誓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏搔涝。R本人自食惡果不足惜厨喂,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望庄呈。 院中可真熱鬧蜕煌,春花似錦、人聲如沸诬留。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽文兑。三九已至盒刚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绿贞,已是汗流浹背因块。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留籍铁,地道東北人涡上。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像寨辩,于是被迫代替她去往敵國和親吓懈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354