springboot-单文件下载、多文件下载(zip)

 

一:场景:

项目中需要做批量下载图片的功能。

思路一:后台拿到图片信息再循环的方式,实践后发现行不通,只能下载第一个文件。

原因:当第一次输出流关闭后请求结束,链接关闭,后续的循环已无意义。

思路二:后台不行就在前台进行循环,多次请求。实践后发现还是行不通,仍只能下载一个文件。

原因:没想明白希望大佬看到告知,感谢!

思路三:没办法只能将多个文件压缩后返回,实践成功。

原理:

1.创建压缩流写到服务器上的压缩文件(临时文件)

2.把服务器上要下载的多个文件写入该压缩文件

3.把压缩文件通过输出流传输给客户端(注意:必须在zipoutputstream流关闭后,否则下载下的文件会报不可预料的压缩文件末端)

4.删除服务器上的临时文件

二:遇到的问题:

1.一开始使用ajax请求方式传送请求,即使后台不报错浏览器也没有进行下载操作?

解释:因为response的原因,一般请求浏览器是会处理服务器输出的response,文件的下载是以二进制形式进行的。但是ajax请求只是个“字符型”的请求,即请求的内容是以文本类型存放的。所以虽然可以读取到返回的response,但只是读取而已,是无法执行的,说白点就是js无法调用到浏览器的下载处理机制和程序。

解决:

window.location.href = "/imgUpload/imgdownload?names="+names+"&paths="+paths;

2.使用ajax传输数组到后台时,后台接收到的值为null

解决:添加traditional : true

解释:jQuery需要调用jQuery.param序列化参数,默认traditional为false,就是说jquery会深度序列化参数对象,但servelt api无法处理深度序列化的参数对象,所以通过设置traditional 为true阻止深度序列化。

3.通过window.location.href拼接数组参数时向后台传递会报错。

解决:先对参数进行编码后再传递var paths = encodeURI(encodeURI(imgUrlList));后台配合解码可以获得编码前的内容

后端解码:String realFileName = java.net.URLDecoder.decode(names[i],"UTF-8");

原因:传递的参数中包含特殊的字符,不符合url的规范,所以先对其进行编码。值得一提的是编码后可以直接传递数组。

之后就是后台代码的编写了

4.返回文件名中文乱码。

response.setHeader( "Content-Disposition", "attachment;filename=" + new String( fileName.getBytes("gb2312"), "ISO8859-1" ) );

参考:https://www.cnblogs.com/feiqihang/p/3544498.html

单文件:

 
  1. public void downImg(HttpServletResponse response,String filename,String path ){

  2. if (filename != null) {

  3. FileInputStream is = null;

  4. BufferedInputStream bs = null;

  5. OutputStream os = null;

  6. try {

  7. File file = new File(path);

  8. if (file.exists()) {

  9. //设置Headers

  10. response.setHeader("Content-Type","application/octet-stream");

  11. //设置下载的文件的名称-该方式已解决中文乱码问题

  12. response.setHeader("Content-Disposition","attachment;filename=" + new String( filename.getBytes("gb2312"), "ISO8859-1" ));

  13. is = new FileInputStream(file);

  14. bs =new BufferedInputStream(is);

  15. os = response.getOutputStream();

  16. byte[] buffer = new byte[1024];

  17. int len = 0;

  18. while((len = bs.read(buffer)) != -1){

  19. os.write(buffer,0,len);

  20. }

  21. }else{

  22. String error = Base64Util.encode("下载的文件资源不存在");

  23. response.sendRedirect("/imgUpload/imgList?error="+error);

  24. }

  25. }catch(IOException ex){

  26. ex.printStackTrace();

  27. }finally {

  28. try{

  29. if(is != null){

  30. is.close();

  31. }

  32. if( bs != null ){

  33. bs.close();

  34. }

  35. if( os != null){

  36. os.flush();

  37. os.close();

  38. }

  39. }catch (IOException e){

  40. e.printStackTrace();

  41. }

  42. }

  43. }

  44. }

多文件下载:下载的是zip格式

前段js代码+后端controller代码

 
  1. /**

  2.      * 图片下载

  3.      */

  4.     function downloadimg() {

  5.         //获取所有被选的图片的名称与绝对路径放入数组

  6.         var list = $(".activecheck");

  7.         var imgNameList = [];

  8.         var imgUrlList = [];

  9.         for(var i = 0;i

  10.             var b = $(list[i].childNodes[2]).attr("data-url");

  11.             imgNameList.push(list[i].childNodes[3].innerText);//图片名称

  12.             imgUrlList.push(b);//图片绝对路径

  13.         }

  14.         var paths =  encodeURI(encodeURI(imgUrlList));

  15.         var names = encodeURI(encodeURI(imgNameList));

  16.         //将名称传入后台

  17.         window.location.href = "/imgUpload/imgdownload?names="+names+"&paths="+paths;

  18.     }

 
  1. /**

  2. * 下载

  3. * @param response

  4. */

  5. @RequestMapping(value = "/imgdownload", method = RequestMethod.GET)

  6. public void imgDownload(@SessionAttribute(MyInterceptor.SESSION_KEY) SessionInfo info,HttpServletResponse response,String [] names,String [] paths) {

  7. //存放--服务器上zip文件的目录

  8. String directory = "D:\\repository\\"+info.getName();

  9. File directoryFile=new File(directory);

  10. if(!directoryFile.isDirectory() && !directoryFile.exists()){

  11. directoryFile.mkdirs();

  12. }

  13. //设置最终输出zip文件的目录+文件名

  14. SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");

  15. String zipFileName = formatter.format(new Date())+".zip";

  16. String strZipPath = directory+"\\"+zipFileName;

  17.  
  18. ZipOutputStream zipStream = null;

  19. FileInputStream zipSource = null;

  20. BufferedInputStream bufferStream = null;

  21. File zipFile = new File(strZipPath);

  22. try{

  23. //构造最终压缩包的输出流

  24. zipStream = new ZipOutputStream(new FileOutputStream(zipFile));

  25. for (int i = 0; i

  26. //解码获取真实路径与文件名

  27. String realFileName = java.net.URLDecoder.decode(names[i],"UTF-8");

  28. String realFilePath = java.net.URLDecoder.decode(paths[i],"UTF-8");

  29. File file = new File(realFilePath);

  30. //TODO:未对文件不存在时进行操作,后期优化。

  31. if(file.exists())

  32. {

  33. zipSource = new FileInputStream(file);//将需要压缩的文件格式化为输入流

  34. /**

  35. * 压缩条目不是具体独立的文件,而是压缩包文件列表中的列表项,称为条目,就像索引一样这里的name就是文件名,

  36. * 文件名和之前的重复就会导致文件被覆盖

  37. */

  38. ZipEntry zipEntry = new ZipEntry(realFileName);//在压缩目录中文件的名字

  39. zipStream.putNextEntry(zipEntry);//定位该压缩条目位置,开始写入文件到压缩包中

  40. bufferStream = new BufferedInputStream(zipSource, 1024 * 10);

  41. int read = 0;

  42. byte[] buf = new byte[1024 * 10];

  43. while((read = bufferStream.read(buf, 0, 1024 * 10)) != -1)

  44. {

  45. zipStream.write(buf, 0, read);

  46. }

  47. }

  48. }

  49. } catch (Exception e) {

  50. e.printStackTrace();

  51. } finally {

  52. //关闭流

  53. try {

  54. if(null != bufferStream) bufferStream.close();

  55. if(null != zipStream){

  56. zipStream.flush();

  57. zipStream.close();

  58. }

  59. if(null != zipSource) zipSource.close();

  60. } catch (IOException e) {

  61. e.printStackTrace();

  62. }

  63. }

  64. //判断系统压缩文件是否存在:true-把该压缩文件通过流输出给客户端后删除该压缩文件 false-未处理

  65. if(zipFile.exists()){

  66. downImg(response,zipFileName,strZipPath);

  67. zipFile.delete();

  68. }

  69. }

你可能感兴趣的:(50,工作中基础学习)