原文:
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;
}
}