这里写自定义目录标题
记一次后台调用文件下载以及调试过程中遇到的问题
一、 业务环境
后台需要提供一个文件下载接口,提供下载接口的服务器与需要下载的资源文件服务器不是同一台,所以需要后台调用下载接口然后返回文件流到前端。
二、代码
1、资源服务器下载接口业务
/**
* 资源服务下载接口
**/
@RequestMapping(value = "/downloadFile", method = RequestMethod.GET)
public void downloadFile(@RequestParam("path") String path, HttpServletRequest request, HttpServletResponse response) throws Exception;
/**
* 资源服务下载接口实现
**/
public void downloadFile(String path, HttpServletRequest request, HttpServletResponse response) throws Exception {
FileInputStream inputStream = null;
try {
if (StringUtils.isNull(path)){
throw new RuntimeException("缺少文件路径!");
}
String name = path;
path = URLDecoder.decode(path,"UTF-8");
if (path.contains("/")){
name = path.substring(path.lastIndexOf("/"),path.length()).replace("/","");
}
String tempBimSavePath = bimSavePath + path;
File bimFile = new File(tempBimSavePath);
response.setContentLengthLong(bimFile.length());
FileDownloadUtil.execute(request,response,bimFile,name);
}catch (Exception e){
log.error("DXBimServiceImpl-download",e);
}finally {
if (inputStream != null){
inputStream.close();
}
}
}
2、提供下载接口
/**
* 批量下载
*
* @return ResultJson
*/
@RequestMapping("/batchDownload")
public void batchDownload(@RequestParam Map<String, Object> reqMap) {
File compress = null;
try {
String fileIds = (String) reqMap.get("fileIds");
Integer prjId = this.getUserAuth().getPrjId();
Assert.isTrue(StringUtils.isNotNull(fileIds),"缺少必要参数!");
String[] split = fileIds.split("\\,");
// List<String> fileIdList = JSONArray.parseArray(fileIds, String.class);
List<File> files = _Xfd02ProjectDocService.getBatchDownloadFiles(Arrays.asList(split),prjId);
compress = ZipUtil.compress(String.format("%s%s.zip", "/", request.getRequestedSessionId()), true, files.toArray(new File[files.size()]));
FileDownloadUtil.execute(request, response, compress, String.format("%s.%s", "模型文件", "zip"));
}catch (Exception e){
this.printErrorLog("/xfd02ProjectDoc/batchDownload",e);
}finally {
try {
compress.delete();
} catch (Exception e) {
this.printErrorLog("/xfd02ProjectDoc/batchDownload compress.delete()",e);
}
}
}
public List<File> getBatchDownloadFiles(List<String> fileIdList, Integer prjId) throws Exception{
List<Xfd02ProjectDoc> xfd02ProjectDocs = this.createQuery(prjId, null)
.set("XFD02_PROJECT_ID", prjId)
.set("XFD02_DOC_ID", fileIdList, "in", LikeOperater.In)
.listModel();
List<File> files = new ArrayList<>();
Map<String,Integer> fileNameMap = new HashMap<>();
for (Xfd02ProjectDoc xfd02ProjectDoc : xfd02ProjectDocs) {
String xfd02Name = xfd02ProjectDoc.getXfd02Name();
Integer integer = fileNameMap.get(xfd02Name);
if (integer == null){
fileNameMap.put(xfd02Name,0);
}else {
fileNameMap.put(xfd02Name,integer++);
if (xfd02Name.contains(".")){
String suffix = xfd02Name.substring(xfd02Name.lastIndexOf("."), xfd02Name.length());
xfd02Name = xfd02Name.replace(suffix,"") + integer + suffix;
}else {
xfd02Name = xfd02Name + integer;
}
}
//请求文件
files.add(downloadDXBimFile(xfd02ProjectDoc.getXfd02UuidName(),xfd02Name));
}
return files;
}
public File downloadDXBimFile(String uuidName,String fileName) throws Exception{
// 替换bucketName
File file = null;
CloseableHttpResponse response = null;
try {
String remoteUrl = DXBIM_SERVER_HOST + DXBimServerAPI.DOWNLOAD_FILE + "?path=" + BUCKET_NAME + "/" + uuidName;
String timeMillis = String.valueOf(SignUtil.getTimestamp());
String sign = SignUtil.sign(timeMillis);
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet get = new HttpGet(remoteUrl);
get.addHeader("sign",sign);
get.addHeader("tick",timeMillis);
response = httpclient.execute(get);
InputStream inputStream = response.getEntity().getContent();
file = IOUtils.inputStream2File(inputStream,fileName);
}catch (Exception e){
log.error("Xfd02ProjectDocService/downloadDXBimFile",e);
}finally {
if (response != null) {
response.close(); //response的关流操作必须在接收完文件后,否则会报文件接口不完整异常
}
}
return file;
}
三、异常
Caused by: java.io.IOException: 你的主机中的软件中止了一个已建立的连接。 at...connectionClosedException:premature end of chunk coded message body:closing chunk expected... 数据流提前被关闭,数据流没有接收完全at sun.nio.ch.SocketDispatcher.write0(Native Method) ~[na:1.8.0_144]
第一、二个异常:
- 原因:后台调用下载接口,数据流写回需要时间,在没有响应完数据的时候关闭response, 提供资源下载的接口会报中止连接,而接收接口则会报数据流未接收完全异常。
第三个异常
- 原因:springboot项目在启动时会自动加载数据源,包含mysql数据源,redis数据源,如果没有启动相应的实例,或者没有排除相应的启动类,则会报连接异常。
@SpringBootApplication(scanBasePackages = "com.icbi", exclude = DataSourceAutoConfiguration.class)。
本文记录了一次后台实现文件下载接口时遇到的问题。由于后台服务器与资源服务器分离,通过调用下载接口返回文件流到前端。在调试过程中,遇到了包括数据流提前关闭导致的'你的主机中的软件中止了一个已建立的连接'和'数据流提前被关闭,数据流没有接收完全'的异常。问题根源在于响应未完全完成就关闭了响应流。同时,还提到了SpringBoot启动时可能出现的数据源连接异常,解决方案是排除数据源自动配置。
2万+

被折叠的 条评论
为什么被折叠?



