外贸网站源码是什么如何在百度提交自己的网站
这两天接了一个需求,让我把服务器上无序的图片资源按 班级、姓名 目录结构进行压缩
任务 | 解释 |
---|---|
场景 | 老师发布某活动(对应activityId),学生可以在上面提交作品(workId) 每个作品可以对应多张图片(资源路径List<String>) |
需求 | 传入一个指定的活动activityId,将这个活动下的作品,分班级、姓名 作为层级关系打包至zip压缩包中 |
要点1 :自定义压缩包的层级关系 | 主要是要自定义层级关系,因此需要在DAO层得到的dto中就开始结构的设计以方便遍历 当时卡在不知道如何指定一个层级关系,后来不断尝试发现ZipOutPutStream中的putZipEntry()形参中可以通过 dir1//dir2//picture.png来指定生成的zip中的层级关系, 但是前提是,整个过程完成之前不能关闭流 |
要点2:直接把生成的结果放入响应体,不存入服务器 | 因为作品时常变动,生成的zip不一定是最终的,所以不能放入服务器。因此直接放入HttpServletResponse.getOutPutStream()中 |
省略一些步骤 | 需求中有大量的业务相关,十分繁琐的步骤,因此这个帖子直接贴一个demo(在自己的电脑上做一个demo) |
注意 | 这个demo不是现成的工具类,使用时需要自己改造 |
1.压缩流ZipOutPutStream介绍
主要是介绍两个重要API
putNextEntry( ZipEntry entry)
,在流结束之前,存入下一个要加入压缩包的文件(不是文件夹),并命名(zip包中该文件的名字)new ZipEntry(String name)
,指定下一个压缩文件的名字
大概的使用流程是:
File zip = new File(输出路径);//输出路径FileOutputStream zipFOS = new FileOutputStream(zip);//输出流ZipOutputStream zipZipOS = new ZipOutputStream(zipFOS);//压缩流BufferedOutputStream zipBufferOS = new BufferedOutputStream(zipZipOS);//缓冲流
虽然最外层是缓冲流,但上面两个API还是针对于压缩流的对象进行操作
zipZipOS.putNextEntry(new ZipEntry("一年级1班\\张三\\1.png"));
zipZipOS.putNextEntry(new ZipEntry("一年级1班\\李四\\2.png"));
zipZipOS.putNextEntry(new ZipEntry("一年级2班\\王八\\3.png"));
然后用最外层的缓冲流进行读写
BufferedInputStream fileBIS = new BufferedInputStream( //缓冲输入流new FileInputStream(new File(资源路径));len = 0;while( (len = fileBIS.read(buffer)) != -1){zipBufferOS.write(buffer);}
最后关闭 最外层流,就可以了(关闭最外层流的时候内层会自动优先关闭,只需要写一遍)
fileBIS.close(); //最外层缓冲输入流,调用几次就开 关 几次zipBufferOS.close();//最外层缓冲输出流,最后关闭一次即可
2.demo演示
看main方法可知,将我电脑上的无序资源,前三个放在一年级1班,后三个放在二年级2班
这里的演示为了更清晰,没有使用try-catch
2.1控制层
@RestController
@RequestMapping("download")
public class ToZipController {@AutowiredZipService zipService;//获取dots 处理文件压缩@GetMapping("/zip")public String getZipFromServerToResponse(HttpServletResponse res) throws IOException {List<Dto> dtoList = zipService.getDtoList();//在处理输出流之前添加 //res.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("活动测试") + ".zip");//可以设置文件输出的名字以及格式,测试后适配所有主流浏览器zipService.doCompressToResponse(dtoList,res.getOutputStream());return "succeed";}@GetMapping("/zip/server")public String getZipFromServerToServer(HttpServletResponse res) throws IOException{List<Dto> dtoList = zipService.getDtoList();zipService.doCompressToDestZip(dtoList,"D:\\pic_test\\destzip.zip");return "succeed";}
2.2Dto实体类
@Data
@AllArgsConstructor
public class Dto {private String activityId;//活动id
private String activityName;//活动名称
private String className;//班级名称
private String studentName;//学生姓名
private String url;//图片地址}
2.3业务层
2.3.1模拟获取List<Dto>
/*** 获取将需要被压缩的图片的基本信息*/public List<Dto> getDtoList() throws IOException {ArrayList<Dto> dtos = new ArrayList<>();dtos.add(new Dto("11","分享你的假期生活","一年级1班","张三","D:\\pic_test\\1.png"));dtos.add(new Dto("11","分享你的假期生活","一年级1班","张三","D:\\pic_test\\2.png"));dtos.add(new Dto("11","分享你的假期生活","一年级1班","李四","D:\\pic_test\\3.png"));dtos.add(new Dto("11","分享你的假期生活","九年级2班","王八","D:\\pic_test\\4.png"));dtos.add(new Dto("11","分享你的假期生活","九年级2班","王八","D:\\pic_test\\5.png"));dtos.add(new Dto("11","分享你的假期生活","九年级2班","王八","D:\\pic_test\\6.png"));return dtos;}
2.3.2压缩方法1:写入HttpServletResponse
/** 方案1:写入response,不指定输出zip的位置* 通过todoList中的信息来分层级压缩,放入res.outPutStream()*/public void doCompressToResponse(List<Dto> todoList, OutputStream os) throws IOException{System.out.println(111);//1.开启流ZipOutputStream zipOS = new ZipOutputStream(os);BufferedOutputStream bufZipOS = new BufferedOutputStream(zipOS);//2.遍历Iterator<Dto> iterator = todoList.iterator();while(iterator.hasNext()){Dto next = iterator.next();//2.1下一个待压缩的元素/**** 根据业务需要,可以针对文件生成的名字进行修改**/zipOS.putNextEntry(new ZipEntry(next.getActivityName()+"\\"+next.getClassName()+"\\"+next.getStudentName()+"\\"+UUID.randomUUID().toString()+".png"));//2.2获取输入流 (缓冲) 根据urlBufferedInputStream fileBIS = new BufferedInputStream(new FileInputStream( //根据业务,如果允许,可以直接传byte[]new File(next.getUrl())));//2.3写入int len = 0;while( (len = fileBIS.read(BUFFER)) != -1){//fileBIS读取流到BUFFER数组bufZipOS.write(BUFFER); //BUFFER数组内容写入到bufZipOS中}//2.4每次写完之后一定要flush刷新bufZipOS.flush();//2.4关闭每次的输入流fileBIS.close();}//3.遍历结束,全部完成,此时关闭zip流bufZipOS.close();//关最外层即可}
需要十分注意的是:
- 遍历循环时,每次开启、关闭一个输入流 ;
- 当所有元素遍历结束之后,才会关闭输出流
- 遍历时,每次读取一个文件都需要刷新输出流,否则会导致内部文件无法打开
2.3.3压缩方法2:指定位置持久化
-
此接口可以通过返回一个url,让前端异步获取资源
-
方案1 2 其实大体上差不多,区别在于输出流的指定,一个是直接res.outPutStream() 一个是通过new File()来持久化
/*** 方案2:形参是一个地址,直接持久化* 而不是写入response响应**/public void doCompressToDestZip(List<Dto> todoList, String destZip) throws IOException{//1.开启流File file = new File(destZip);FileOutputStream fIS = new FileOutputStream(file);ZipOutputStream zipOS = new ZipOutputStream(fIS);BufferedOutputStream bufZipOS = new BufferedOutputStream(zipOS);//2.遍历Iterator<Dto> iterator = todoList.iterator();while(iterator.hasNext()){Dto next = iterator.next();/**** 根据业务需要,可以针对文件生成的名字进行修改**///2.1下一个待压缩的元素zipOS.putNextEntry(new ZipEntry(next.getActivityName()+"\\"+next.getClassName()+"\\"+next.getStudentName()+"\\"+UUID.randomUUID().toString()+".png"));//2.2获取输入流 (缓冲) 根据urlBufferedInputStream fileBIS = new BufferedInputStream(new FileInputStream( //根据业务,如果允许,可以直接传byte[]new File(next.getUrl())));//2.3写入int len = 0;while( (len = fileBIS.read(BUFFER)) != -1){//fileBIS读取流到BUFFER数组bufZipOS.write(BUFFER); //BUFFER数组内容写入到bufZipOS中}//2.4关闭遍历时每次的输入流fileBIS.close();}//3.遍历结束,全部完成,此时关闭zip流bufZipOS.close();//关最外层即可}