前言

实现文件打包成zip下载,支持zip包含目录、文件。废话不多说,直接上码。


一、设计思路

  1. 后端组织文件,打包成zip
  2. 上传到OSS存储
  3. 返回文件名称给前端
  4. 前端根据返回的文件名称(url)直接从oss服务下载(这样做可以减轻业务服务器的压力,但是注意OSS服务上的文件注意定期清理)

二、核心代码

oss实现这里就不多说了,这里实际上还是涉及到一些契约编程。
impl实现方法

@Override
    public String exportAppAlbum(ConstructionAlbumInfoVo constructionAlbumInfoVo) {
        String fileName = null;
        String albumName = exportEventImgs(constructionAlbumInfoVo, fileExtList);
        List<FileExt> fileExtList = new ArrayList<>();
       
        //开始导出
        InputStream fileInputStream = null;
        try {
            String today = DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_MS_FORMAT);
            fileInputStream = ZipFileUtil.getFileInputStream(today, fileExtList);
            //上传文件到oss
            String resultFilename =
                    albumName + "_" + today + ".zip";
            ossClient.putObject(bucket, resultFilename, fileInputStream, "application/octet-stream");
            fileName = ossService.generateUrl(resultFilename);
        } catch (Exception e) {
            log.error("打包失败:", e);
            throw new BusinessException("下载文件打包失败");
        } finally {
            // 关闭流
            try {
                if (null != fileInputStream) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                log.error("打包失败:", e);
            }
        }
        return fileName;
    }

exportEventImgs方法

private String exportEventImgs(ConstructionAlbumInfoVo constructionAlbumInfoVo, List<FileExt> fileExtList) {
        String ids = constructionAlbumInfoVo.getIds();
        List<String> idList = Stream.of(ids.split(",")).collect(Collectors.toList());
        AtomicReference<Long> albumId = new AtomicReference<>();
        idList.stream().forEach(d -> {
            //查询事件信息
            Long eventId = Long.parseLong(d);
            ConstructionAlbum event = baseMapper.selectById(eventId);
            //查询相册信息
            albumId.set(event.getParentId());

            //查询照片信息
            QueryWrapper<ConstructionAlbum> qw = new QueryWrapper<>();
            qw.eq("parent_id", eventId);
            qw.eq("data_type", 3);
            qw.eq("delete_state", 1);
            List<ConstructionAlbum> imgList = baseMapper.selectList(qw);
            if (CollectionUtil.isNotEmpty(imgList)) {

                AtomicInteger count = new AtomicInteger(1);
                imgList.stream().forEach(a -> {
                    String url = a.getSourceUrl();
                    if (StringUtils.isNotEmpty(url)) {
                        url = url.replace("+", "%2B");
                        url = url.replace("&", "%26");
                        FileExt fileInfoByUrl = ExportUtil                      
                                .getFileInfoByUrl(url, event.getName(), DateUtil.format(event.getCreateTime(), DatePattern.PURE_DATETIME_MS_FORMAT), a.getName(), (long) count.get());
                        if (null != fileInfoByUrl && null != fileInfoByUrl.getInputStream()) {
                             //设置文件名称
                            fileInfoByUrl.setFileName(a.getName());
                            //设置目录名称
                            fileInfoByUrl.setFolder(event.getName());
                            fileExtList.add(fileInfoByUrl);
                            count.getAndIncrement();
                        } else {
                            log.error("打包失败:名称【{}】", url);
                        }
                    }
                });
            }
        });

        ConstructionAlbum album = baseMapper.selectById(albumId.get());
        return album.getName();
    }

实际上就是根据选中的id,查询文件的事件、相册名称,事件名称作为目录、相册下的照片作为文件名称。
FileExt扩展对象

@Data
public class FileExt {

    //文件流
    private InputStream inputStream;

    //文件名称
    private String fileName;
    //文件大小
    private String fileSize;
    //文件目录名称
    private String folder;

}

ExportUtil工具类

@Slf4j
public class ExportUtil {

  public static FileExt getFileInfoByUrl(String photoUrl,String prefix,String createTime,String fullName,Long count){
    InputStream is = null;
    try {
      // 构造URL
      URL url = new URL(photoUrl);
      // 打开连接
      URLConnection con = url.openConnection();
      //设置请求超时为5s
      con.setConnectTimeout(5 * 1000);
      // 输入流
      is = con.getInputStream();
      FileExt zipFileExt = new FileExt();
      //对url路径做截取,这里实际上还是一个契约命名规则,大家可以根据自己的业务调整
      String folder = prefix + "_" + fullName.split("/")[0] + "_" + createTime;
      String suffix = photoUrl.substring(photoUrl.lastIndexOf("."), photoUrl.length());
      String fileName = folder + "_"+ count + suffix;
      zipFileExt.setFileName(fileName);
      zipFileExt.setInputStream(is);
      zipFileExt.setFolder(folder);
      return zipFileExt;
    } catch (Exception e){
      log.error("下载图片失败:名称【{}】", photoUrl);
    }

    return null;
  }

}

ZipFileUtil工具类

@Slf4j
public class ZipFileUtil {

    public static String zipSuffix = ".zip";
    public static String fileNameField = "x-oss-meta-filename";

    /**
     *
     * @param fileName  压缩后的文件名称:测试.zip
     * @param fileExtList    子文件名称 + 文件流
     * @return
     */
    public static File compressionZipFile(String fileName, List<FileExt> fileExtList){
        File zipFile = null;
        try {
            zipFile = File.createTempFile(fileName, zipSuffix);
            FileOutputStream f = new FileOutputStream(zipFile);
            ZipOutputStream zos = new ZipOutputStream(f);

            //去重
            Map<String, List<FileExt>> collect = fileExtList.stream().collect(Collectors.groupingBy(FileExt::getFileName));
            fileExtList.clear();
            for (Map.Entry<String, List<FileExt>> entry : collect.entrySet()){
                fileExtList.add(entry.getValue().get(0));
            }

            for (FileExt zfe : fileExtList) {
                InputStream inputStream = zfe.getInputStream();
                if(StringUtils.isEmpty(zfe.getFolder())){
                    zfe.setFolder("/");
                }else{
                    zfe.setFolder(zfe.getFolder() + "/");
                }
                zos.putNextEntry(new ZipEntry(zfe.getFolder() + zfe.getFileName()));
                byte[] buf = new byte[1024 * 1024];
                int size = 0;
                while ((size = inputStream.read(buf)) != -1) {
                    zos.write(buf, 0, size);
                }
                inputStream.close();
                zos.closeEntry();
            }
            zos.close();
        } catch (IOException e) {
            log.error("压缩文件是报错:{}",e.getMessage());
            e.printStackTrace();
        }
        return zipFile;
    }

    public static InputStream getFileInputStream(String fileName, List<FileExt> zipFileExtList) throws Exception {
        File file = compressionZipFile(fileName, zipFileExtList);
        return new FileInputStream(file);
    }

    /**
     *根据网络文件路径获取输入流
     * @param url
     * @return
     */
    public static InputStream getImageStream(String url) {
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setReadTimeout(5000);
            connection.setConnectTimeout(5000);
            connection.setRequestMethod("GET");
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                InputStream inputStream = connection.getInputStream();
                return inputStream;
            }
        } catch (IOException e) {
            log.error("获取网络图片出现异常,图片路径为:{}.异常信息为:{}", url, e.getMessage());
            System.out.println("获取网络图片出现异常,图片路径为:" + url);
            e.printStackTrace();
        }
        return null;
    }

    public static FileExt getFileInfoByUrl(String url){
        FileExt zipFileExt = new FileExt();
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            //30s
            connection.setReadTimeout(30000);
            connection.setConnectTimeout(30000);
            connection.setRequestMethod("GET");
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                String fileSize = getFileSize(connection.getContentLengthLong());
//                String fileName = connection.getHeaderField(fileNameField);
                //对url路径做截取
                String fileName = url.substring(url.lastIndexOf("/") + 1, url.length());
                InputStream inputStream = connection.getInputStream();
                zipFileExt.setFileSize(fileSize);
                zipFileExt.setFileName(fileName);
                zipFileExt.setInputStream(inputStream);
            }
        } catch (IOException e) {
            log.error("获取网络图片出现异常,图片路径为:{}.异常信息为:{}", url, e.getMessage());
            System.out.println("获取网络图片出现异常,图片路径为:" + url);
            e.printStackTrace();
        }
        return zipFileExt;
    }

    public static String getFileUrlSize(String url){
        String result = "0";
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setReadTimeout(5000);
            connection.setConnectTimeout(5000);
            connection.setRequestMethod("GET");
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                result = getFileSize(connection.getContentLengthLong());
            }
        } catch (IOException e) {
            log.error("获取网络图片出现异常,图片路径为:{}.异常信息为:{}", url, e.getMessage());
            System.out.println("获取网络图片出现异常,图片路径为:" + url);
            e.printStackTrace();
        }
        return result;
    }

    public static String getFileNameByURL(String url){
        String result = "";
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setReadTimeout(5000);
            connection.setConnectTimeout(5000);
            connection.setRequestMethod("GET");
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                result = connection.getHeaderField(fileNameField);
            }
        } catch (IOException e) {
            log.error("获取网络图片出现异常,图片路径为:{}.异常信息为:{}", url, e.getMessage());
            System.out.println("获取网络图片出现异常,图片路径为:" + url);
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 字节转kb/mb/gb
     * @param size
     * @return
     */
    public static String getFileSize(long size) {
        //以B为单位
        if (size < 1024) {
            return String.valueOf(size) + "B";
        } else {
            size = size / 1024;
        }
        //以KB为单位
        if (size < 1024) {
            return String.valueOf(size) + "KB";
        } else {
            size = size / 1024;
        }
        if (size < 1024) {
            //以MB为单位
            size = size * 100;
            return String.valueOf((size / 100)) + "."
                    + String.valueOf((size % 100)) + "MB";
        } else {
            //以GB为单位的,先除于1024再作同样的处理
            size = size * 100 / 1024;
            return String.valueOf((size / 100)) + "."
                    + String.valueOf((size % 100)) + "GB";
        }
    }
}

总结

  • 其实没啥好总结的,也就是将zip打包、OSS上传下载做了一个组合而已。打包zip文件做了服务公共业务提取处理。希望能帮到大家uping