前言
实现文件打包成zip下载,支持zip包含目录、文件。废话不多说,直接上码。
一、设计思路
- 后端组织文件,打包成zip
- 上传到OSS存储
- 返回文件名称给前端
- 前端根据返回的文件名称(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