前后端传值响应下载文件压缩包
前端代码:
// 多分文件压缩成一个压缩包下载
BatchDownloadFile() {
debugger;
let ids = this.frameContext.appContext.frameContextManager.getFrameContextById("data-grid-component").viewModel.uiState['ids'] as string[];
if (ids.length == 0) {
this.msgService.error("请选择需要下载的文件!");
return;
}
// 将选中的文件对应的目录的id拼接成一个长字段串传给后端
let idss = "";
for (let i = 0; i < ids.length; i++) {
idss = idss + ids[i] + ",";
}
debugger;
const repository = this.frameContext.repository as BefRepository<any>;
const requestInfo = repository.restService.buildRequestInfo();
//eapi上增加的BE自定义动作对应的URI的末尾的值
const actionUri = '/api/wxgl/wxtzgl/v1.0/wxtzxz080901_frm/service/exportfiles';
// const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
const body = {
requestInfo: requestInfo,
id: idss // 将选中的数据的id传到后端,用于下载这几条数据对应的改单
}
this.formNotifyService.success("正在下载请稍等!");
this.httpClient.put(actionUri, body, { responseType: 'blob', observe: 'response' }).subscribe(
data => {
const link = document.createElement('a');
const blob = new Blob([data.body], { type: 'application/force-download;charset=UTF-8' }) // 压缩包zip格式
const url = window.URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", '文件压缩包下载.zip');
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}
)
// window.location.href = "http://服务器IP:8090/wx/wxglwxtzxz080901?ids=" + id;
}
// 下载Excel
ExportChangeDetailList() {
debugger;
this.loadingService.show();
const uri = new Uri(this.uriService.extendUri(this.apiUrl) + '/service/downloadchangedetail', {});
const url = uri.toString();
const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
const option = {
responseType: 'blob' as any,
headers: headers,
observe: 'response' as any
}
this.httpClient.put(url, "", option).subscribe((response: any) => {
this.loadingService.hide();
const contentType = "application/octet-stream;charset=utf-8";
let fileName = response.headers.get("Content-Disposition").split(";")[1].split("filename=")[1];
fileName = decodeURI(fileName);
if ('msSave0rOpenBlob' in navigator) {//IE导出
const blob = new Blob([response.body], { type: contentType });
window.navigator.msSaveBlob(blob, fileName);
} else {
this.downloadFile(response, fileName, contentType);
}
},
error => {
this.loadingService.hide();
this.msgService.info("下载失败。");
return EMPTY;
}
);
}
//下载附件
downloadFile(response, fileName, contentType) {
const blob = new Blob([response.body], { type: contentType });
const url = window.URL.createObjectURL(blob);
// 打开新窗口方式进行下载
// window.open(url);
// 以动态创建a标签进行下载
const a = document.createElement('a');
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
}
后端下载文件并用流响应的代码:
public class ExportFilesVOAction extends AbstractFSAction<String> {
private String id;
public ExportFilesVOAction(String id) {
this.id = id;
}
private static final RestTemplate template = new RestTemplate();
@Override
public void execute() {
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
String projectPath = null;
try {
projectPath = new File("..").getCanonicalPath();
} catch (IOException e) {
e.printStackTrace();
}
String fileName = "文件压缩包下载.zip";
String rootFloder = projectPath + "\\" + fileName;
try {
// 调用内部服务 生成Excel
String url = "http://服务器IP:8090/wx/wxglwxtzxz080901?ids=" + this.id;
ResponseEntity<Resource> entity = template.getForEntity(url, Resource.class);
InputStream stream = entity.getBody().getInputStream();
// 响应前端
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletResponse response = servletRequestAttributes.getResponse();
try {
// 清空response
response.reset();
// 以流的形式下载文件。
InputStream fis = new BufferedInputStream(stream);
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
fileName = URLEncoder.encode(fileName, "UTF-8");
response.addHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes()));
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/force-download");
response.setCharacterEncoding("UTF-8");
outputStream.write(buffer);
outputStream.flush();
outputStream.close();
setResult("ok");
} catch (IOException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
File file = new File(rootFloder);
if (file.exists()) {
file.delete();
}
}
}
}
服务器上下载文件:
@RestController
@RequestMapping("/wxglwxtzxz080901")
@Slf4j
@CrossOrigin
public class Wxglwxtzxz080901Controller {
private Wxglwxtzxz080901ServiceImpl wxglwxtzxz080901Service;
@Autowired
public void setWxglwxtzxz080901Service(Wxglwxtzxz080901ServiceImpl wxglwxtzxz080901Service) {
this.wxglwxtzxz080901Service = wxglwxtzxz080901Service;
}
@Value("${wxfile.path}")
private String basePath;
private static final DateTimeFormatter DF = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter DF_DOWN = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH时mm分ss秒");
private SnowFlakeUtil snowFlakeUtil;
@Autowired
public void setSnowFlakeUtil(SnowFlakeUtil snowFlakeUtil) {
this.snowFlakeUtil = snowFlakeUtil;
}
@GetMapping
@ResponseBody
public void downloadFile(@RequestParam("ids") String ids, HttpServletResponse response) {
if (ObjectUtils.isEmpty(ids)) {
return;
}
String pathTime = DF_DOWN.format(LocalDateTime.now());
String targetPath = basePath + pathTime;
File targetFile = new File(targetPath);
if (!targetFile.exists()) {
targetFile.mkdirs();
}
final String[] idList = ids.split(",");
final List<Wxglwxtzxz080901> wxglwxtzxz080901s = wxglwxtzxz080901Service.listByIds(Arrays.asList(idList));
File zipFile = null;
try {
copyTargetFiles(targetPath, wxglwxtzxz080901s);
// 压缩文件
zipFile = new File(targetPath + ".zip");
if (zipFile.exists()) {
zipFile.delete();
}
System.out.println(targetFile);
ZipUtils.toZip(targetFile, zipFile.getAbsolutePath(), true);
// 将下载成功的图纸属性设置为已下载
wxglwxtzxz080901s.forEach((bean) -> {
bean.setSfyxz("是");
bean.setXzrq(DF.format(LocalDateTime.now()));
wxglwxtzxz080901Service.updateById(bean);
});
// 将文件传给前端
FileUtils.downFile(zipFile.getAbsolutePath(), "文件压缩包下载.zip", response);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (zipFile != null && zipFile.exists()) {
zipFile.delete();
}
if (targetFile != null && targetFile.exists()) {
FileUtils.delDirs(targetFile);
targetFile.delete();
}
}
}
private void copyTargetFiles(String targetPath, List<Wxglwxtzxz080901> wxglwxtzxz080901s) throws IOException {
for (Wxglwxtzxz080901 wxglwxtzxz080901 : wxglwxtzxz080901s) {
// 将文件拷贝至统一路径
final String srcPath = basePath + wxglwxtzxz080901.getFjlj();
// 路径层级 工程号/专业
String filePath = targetPath + "/" + wxglwxtzxz080901.getGch() + "/" + wxglwxtzxz080901.getSjzy();
File targetWxFile = new File(filePath);
if (!targetWxFile.exists()) {
targetWxFile.mkdirs();
}
// 拷贝至一个文件夹下 并改名图纸文件夹名称格式
String folderName = "";
if (srcPath.contains("DWG")) {
// DWG表示为初版图纸
folderName = "【初版图纸】" + wxglwxtzxz080901.getGch() + "_图号:" + wxglwxtzxz080901.getTh() + "_图名:" + wxglwxtzxz080901.getTzmc();
} else {
folderName = "【变更图纸】" + wxglwxtzxz080901.getGch() + "_改单号:" + wxglwxtzxz080901.getGdh() + "_图号:" + wxglwxtzxz080901.getTh() + "_图名:" + wxglwxtzxz080901.getTzmc();
}
FileUtils.copyFolder(new File(srcPath), targetWxFile, folderName);
}
}
@GetMapping("/changedetail")
public String downloadChangeTail(@RequestParam("path") String path, HttpServletResponse response) {
QueryWrapper<Wxglwxtzxz080901> wrapper = new QueryWrapper<>();
wrapper.isNotNull("GDH");
String fileName = "D:\\wxFiles\\" + path;
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, Wxglwxtzxz080901.class).sheet("改单清单").doWrite(wxglwxtzxz080901Service.list(wrapper));
try {
FileInputStream inputStream = new FileInputStream(fileName);
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
response.setContentType("video/avi");
path = URLEncoder.encode(path, "UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=\"" + path + "\"");
response.setContentLength(data.length);
response.setHeader("Content-Range", "" + Integer.valueOf(data.length - 1));
response.setHeader("Accept-Ranges", "bytes");
OutputStream os = response.getOutputStream();
os.write(data);
//先声明的流后关掉!
os.flush();
os.close();
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
File file = new File(fileName);
if (file.exists()) {
file.delete();
}
}
return path;
}
}
public class ZipUtils {
private static final int BUFFER_SIZE = 2 * 1024;
/**
* 压缩成ZIP 方法1
* @param sourceFile 压缩文件夹
* @param zipFilePathName 压缩文件输出地址与名字
* @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;
* false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
* @throws RuntimeException 压缩失败会抛出运行时异常
*/
public static void toZip( File sourceFile, String zipFilePathName, boolean KeepDirStructure)
throws RuntimeException, FileNotFoundException {
long start = System.currentTimeMillis();
File file = new File(zipFilePathName);
if (!file.exists()){
file.getParentFile().mkdirs();
}
FileOutputStream out = new FileOutputStream(zipFilePathName);
ZipOutputStream zos = null ;
try {
zos = new ZipOutputStream(out);
compress(sourceFile,zos,sourceFile.getName(),KeepDirStructure);
long end = System.currentTimeMillis();
System.out.println("压缩完成,耗时:" + (end - start) +" ms");
} catch (Exception e) {
throw new RuntimeException("zip error from ZipUtils",e);
}finally{
if(zos != null){
try {
zos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 压缩成ZIP 方法2
* @param srcFiles 需要压缩的文件列表
* @param zipFilePathName 压缩文件输出地址与名字
* @throws RuntimeException 压缩失败会抛出运行时异常
*/
public static void toZip(List<File> srcFiles , String zipFilePathName) throws RuntimeException, FileNotFoundException {
long start = System.currentTimeMillis();
ZipOutputStream zos = null ;
File file = new File(zipFilePathName);
if (!file.exists()){
file.getParentFile().mkdirs();
}
FileOutputStream out = new FileOutputStream(zipFilePathName);
try {
zos = new ZipOutputStream(out);
for (File srcFile : srcFiles) {
byte[] buf = new byte[BUFFER_SIZE];
zos.putNextEntry(new ZipEntry(srcFile.getName()));
int len;
FileInputStream in = new FileInputStream(srcFile);
while ((len = in.read(buf)) != -1){
zos.write(buf, 0, len);
}
zos.closeEntry();
in.close();
}
long end = System.currentTimeMillis();
System.out.println("压缩完成,耗时:" + (end - start) +" ms");
} catch (Exception e) {
throw new RuntimeException("zip error from ZipUtils",e);
}finally{
if(zos != null){
try {
zos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 递归压缩方法
* @param sourceFile 源文件
* @param zos zip输出流
* @param name 压缩后的名称
* @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;
* false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
* @throws Exception
*/
private static void compress(File sourceFile, ZipOutputStream zos, String name,
boolean KeepDirStructure) throws Exception{
byte[] buf = new byte[BUFFER_SIZE];
if(sourceFile.isFile()){
// 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
zos.putNextEntry(new ZipEntry(name));
// copy文件到zip输出流中
int len;
FileInputStream in = new FileInputStream(sourceFile);
while ((len = in.read(buf)) != -1){
zos.write(buf, 0, len);
}
// Complete the entry
zos.closeEntry();
in.close();
} else {
File[] listFiles = sourceFile.listFiles();
if(listFiles == null || listFiles.length == 0){
// 需要保留原来的文件结构时,需要对空文件夹进行处理
if(KeepDirStructure){
// 空文件夹的处理
zos.putNextEntry(new ZipEntry(name + "/"));
// 没有文件,不需要文件的copy
zos.closeEntry();
}
}else {
for (File file : listFiles) {
// 判断是否需要保留原来的文件结构
if (KeepDirStructure) {
// 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,
// 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了
compress(file, zos, name + "/" + file.getName(), true);
} else {
compress(file, zos, file.getName(), false);
}
}
}
}
}
}
@Slf4j
public class FileUtils {
/**
* 复制文件
*
* @param srcFile
* @param destFile
* @throws IOException
*/
public static void copyFile(File srcFile, File destFile) throws IOException {
// 创建字节缓冲输入流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
// 创建字节缓冲输出流对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
//一次读入一个字节数组的数据
byte[] bys = new byte[1024];
int len;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
//释放资源
bos.close();
bis.close();
}
/**
* 复制文件夹
*
* @param srcFile
* @param destFile
* @throws IOException
*/
public static void copyFolder(File srcFile, File destFile, String folderName) throws IOException {
//如果srcFile为目录,首次调用即是判断是否为目录
if (srcFile.isDirectory()) {
// 获取目录名,首次调用时目录名为
String srcFileName;
if (ObjectUtils.isEmpty(folderName)) {
srcFileName = srcFile.getName();
} else {
srcFileName = folderName;
}
// 在destFile下新建一个名为srcFile的目录,首次调用时即在code2目录下创建test目录
File newFolder = new File(destFile, srcFileName);
// 如果目录不存在,则新建目录
if (!newFolder.exists()) {
newFolder.mkdirs();
}
// 将srcFile目录下的文件或目录存入文件数组中
File[] listFiles = srcFile.listFiles();
// 遍历test目录下所有的文件
for (File file : listFiles) {
// 利用递归将file作为srcFile,newFolder作为destFile调用copyFolder方法
copyFolder(file, newFolder, null);
}
} else {
String srcFileName = srcFile.getName();
File newFile = new File(destFile, srcFileName);
if (!newFile.exists()) {
newFile.createNewFile();
}
copyFile(srcFile, newFile);
}
}
/**
* 下载文件
*
* @param path
* @param fileName
* @param response
*/
public static void downFile(String path, String fileName, HttpServletResponse response) {
File file = new File(path);
response.setCharacterEncoding("utf-8");
String downloadFileName = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
response.setContentType("application/force-download");
// 设置允许跨域的Key
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
response.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
if (file.exists()) {
try {
byte[] buffer = new byte[1024];
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
OutputStream os = response.getOutputStream();
int i = bis.read(buffer);
while (i != -1) {
os.write(buffer, 0, i);
i = bis.read(buffer);
}
bis.close();
} catch (Exception e) {
e.printStackTrace();
log.error(String.valueOf(e.getCause()));
}
}
}
public static void main(String[] args) {
delDirs(new File("aa"));
}
/**
* 递归删除
*
* @param file
* @return
*/
public static Boolean delDirs(File file) {
//判断是否存在
if (!file.exists()) {
return false;
}
//如果是文件就直接删除
if (file.isFile()) {
file.delete();
} else {
//该方法用 File 对象形式返回目录下的所有文件
File[] list = file.listFiles();
for (File f : list) {
if (f.isFile()) {
f.delete();
} else {
delDirs(f);
//删除空目录
f.delete();
}
}
}
return true;
}
}