背景
需求
需要对外提供下载文件的API接口,为了安全对外接口下载接口使用流形式(平时我们会将文件服务器的文件URL返回前端使用)
思路
文件地址生成 本地File文件,流转换之后删除本地文件,响应前端。
环境
- SpringBoot 2.1.2.RELEASE
- JDK 1.8
- Maven 3.5.0
正文
Service
package com.fang.industry.service.exce.impl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
/**
* @author: guochao.bj@fang.com
* @createDate: 2021/6/22 10:26
*/
@Service
public class ILandServiceImpl implements ILandService {
@Resource
IndustryLandApi industryLandApi;
@Resource
ILandDao landDao;
@Value("${landAfficheFilepath}")
private String landAfficheFilepath;
//本地文件暂存位置,使用apollo配置方便修改,也可以在配置文件定义
@Override
public ResultDto getLandAfficheFile(LandDetailBo bo,HttpServletResponse response){
ResultDto result = new ResultDto();
/*---------------------------------具体业务逻辑开始,无关--------------------------------------------*/
try {
com.fang.industry.bo.land.land.LandDetailBo landDetailBo = new com.fang.industry.bo.land.land.LandDetailBo();
BeanUtils.copyProperties(bo,landDetailBo);
DtoResponse<com.fang.industry.dto.land.land.LandDetailDto> landDetail = industryLandApi.getLandDetail(landDetailBo);
if (landDetail == null || landDetail.getData() == null) {
result.setCode(0);
result.setMessage("公告详情查询异常");
return result;
}
com.fang.industry.dto.land.land.LandDetailDto data = landDetail.getData();
/*---------------------------------以上部分为具体业务逻辑结束,无关--------------------------------------------*/
File fileByurl=null;
String announcementNo = data.getAnnouncementNo(); //文件名称
String parcelAffiche = data.getParcelAffiche(); //网络文件地址URL
String suffix=".pdf"; //文件后缀
if (parcelAffiche.endsWith(".pdf")){
suffix=".pdf";
}else if (parcelAffiche.endsWith(".docx")){
suffix=".docx";
}
//核心方法,网络文件地址URL转换为File文件
fileByurl = getFileByurl(parcelAffiche, suffix, announcementNo);
try {
// 以流的形式下载文件。
InputStream fis = new BufferedInputStream(new FileInputStream(fileByurl));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
// 设置response的Header
String fileName = new String(("LandAfficheFile"+suffix).getBytes());
response.setCharacterEncoding("UTF-8");
response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
response.addHeader("Content-Length", "" + fileByurl.length());
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
toClient.write(buffer);
toClient.flush();
toClient.close();
}finally {
/*
* 重要,在将文件转换为文件流之后,要将本地暂存文件删除
* */
fileByurl.delete();
}
result.setCode(1);
result.setMessage("土地公告文件下载成功");
} catch (Exception e) {
e.printStackTrace();
result.setCode(0);
result.setMessage("土地公告文件下载异常");
}
return result;
}
/**
* 网络文件URL转换为文件
* @param url 网络文件URL
* @param suffix 文件后缀
* @param fileName 文件名
* @return java.io.File
* @author guochao.bj@fang.com
* @date 2021/6/23
*/
private File getFileByurl(String url,String suffix,String fileName){
File file = null;
URL urlfile;
InputStream inputStream = null;
OutputStream outputStream= null;
try {
String path = landAfficheFilepath; //本地文件暂存位置,使用apollo配置方便修改,也可以在配置文件定义
File file1 = new File(path);
file = File.createTempFile(fileName, suffix,file1);
//下载
urlfile = new URL(url);
inputStream = urlfile.openStream();
outputStream= new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (Exception e) {
return null;
} finally {
try {
if (null != outputStream) {
outputStream.close();
}
if (null != inputStream) {
inputStream.close();
}
} catch (Exception e) {
return null;
}
}
return file;
}
}
Controller
@ApiSn("7435f8ec-ab8f-4801-b94e-bfa5763e5dd1")
@PostMapping(value = "/getLandAfficheFile", produces = "application/json")
@ApiOperation(value = "土地公告文件接口", notes = "guochao.bj@fang.com", httpMethod = "POST",
produces = "application/json")
void getLandAfficheFile(@RequestBody LandDetailBo bo) throws MalformedURLException;
@Resource
HttpServletResponse response;
@Override
public void getLandAfficheFile(LandDetailBo bo){
if (bo.getCityCode() == null || StringUtils.isBlank(bo.getCityCode().toString())) {
throw new ApiRuntimeException(ApiExceptionEnum.PARAM_IS_BLANK, "城市Code不能为空!");
}
if (StringUtils.isBlank(bo.getLandId())) {
throw new ApiRuntimeException(ApiExceptionEnum.PARAM_IS_BLANK, "土地ID不能为空!");
}
ResultDto resultDto = iLandService.getLandAfficheFile(bo, response);
if (resultDto.getCode()==0){
throw new ApiRuntimeException(ApiExceptionEnum.INTERFACE_INNER_INVOKE_ERROR, resultDto.getMessage());
}
}
补充
/**
* 删除指定目录下面指定文件类型的文件
*
* @param path
*/
public static void delSpecifyTheTypeFile(String path) {
File dir = new File(path);
// 该文件目录下文件全部放入数组
File[] files = dir.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
String fileName = files[i].getName();
// 判断是文件还是文件夹
if (files[i].isDirectory()) {
// 获取文件绝对路径
delSpecifyTheTypeFile(files[i].getAbsolutePath());
// 判断文件名是否以.tif结尾
} else if (fileName.endsWith(".tif")) {
String strFileName = files[i].getAbsolutePath();
System.out.println("删除文件名:" + strFileName);
files[i].delete();
} else {
continue;
}
}
}
}
20210721补充
中文使用URLEncoder编码,前端配合decodeURI解码
编码之前:数据集-下载
之后:%E6%95%B0%E6%8D%AE%E9%9B%86-%E4%B8%8B%E8%BD%BD
@Resource
HttpServletResponse response;
public void downloadData(DataIdDownloadBo bo) {
DtoResponse<DataExcelDto> result=new DtoResponse<DataExcelDto>();
try {
DataExcel dataExcel = appDataService.getDataExcelDetails(bo);
if (dataExcel==null){
return;
}
ArrayList<List<String>> headList = dataExcel.getHeadList();
final Integer[] startrow = {headList.size()};
//ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Workbook workBook = new XSSFWorkbook();
ExportExcelPoiUtil.createHead(headList,"sheet",workBook);
Map<Integer, ArrayList<List<String>>> bodyStringListMap = dataExcel.getBodyStringListMap();
Set<Integer> keySet = bodyStringListMap.keySet();
keySet.forEach(a->{
ArrayList<List<String>> bodyList = bodyStringListMap.get(a);
ExportExcelPoiUtil.fillBody(bodyList,"sheet", startrow[0],dataExcel.getMergeCellIndex(),workBook);
int size = bodyList.size();
startrow[0] = startrow[0] + size;
});
//---------------------------这里下载代码 Strat-------------------------------
ServletOutputStream outputStream = response.getOutputStream();
//URLEncoder编码
String encode = URLEncoder.encode(dataExcel.getDataName(), "UTF-8");
response.addHeader("Content-Disposition", "attachment;filename=" + encode+".xlsx");
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
response.setCharacterEncoding("utf-8");
ExportExcelPoiUtil.exportExcelByStream(workBook,outputStream);
//---------------------------这里下载代码 end-------------------------------
}catch (Exception e){
e.printStackTrace();
//return result.failure(0,"下载异常");
}
}