记一次后台调用文件下载以及调试过程中遇到的问题

本文记录了一次后台实现文件下载接口时遇到的问题。由于后台服务器与资源服务器分离,通过调用下载接口返回文件流到前端。在调试过程中,遇到了包括数据流提前关闭导致的'你的主机中的软件中止了一个已建立的连接'和'数据流提前被关闭,数据流没有接收完全'的异常。问题根源在于响应未完全完成就关闭了响应流。同时,还提到了SpringBoot启动时可能出现的数据源连接异常,解决方案是排除数据源自动配置。

记一次后台调用文件下载以及调试过程中遇到的问题

一、 业务环境

  后台需要提供一个文件下载接口,提供下载接口的服务器与需要下载的资源文件服务器不是同一台,所以需要后台调用下载接口然后返回文件流到前端。

二、代码

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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值