实现的功能:
下载照片到本地文件夹(可做临时缓存文件,最后删除),分类存放照片,压缩成zip下载到指定路径(保留or不保留多级目录结构)。
实现思路
1.创建一个文件夹A,用来存图片
2.将图片转为图片流写入文件夹A
3.将文件夹压缩成ZIP
需要熟悉的类的API:
File、ZipEntry、IO类{FileInputStream、FileOutputStream、ZipOutputStream}、HttpURLConnection
【实际开发:通过response返回自定义下载路径,可参考:https://blog.youkuaiyun.com/FFJ_Olivia/article/details/121102454】
下方代码参考了网上的方法,主要用于练习学习,所以注释非常详细:
package com.epoch.customproject.goer.bill.controller;
import com.epoch.infrastructure.util.service.DateUtils;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @author olivia
*/
public class MyController {
public static void main(String[] args) throws Exception {
// 照片存放的文件路径【相对路径,即当前项目根目录下创建,如果temp前加“\”,则会在盘符下创建temp】
// “\\”是转义符,也可以用File.separator,防止跨平台出现错误
String fileStr = "temp\\image";
// 照片压缩的全量文件路径
String zipFileStr = "temp\\image1.zip";
// 照片存放集合
Map<String, Map<String, Object>> imgsDataMap = new HashMap<>();
Map<String, Object> imgsMap = new HashMap<>();
imgsMap.put("第一张图片.jpg", "http://bpic.588ku.com/element_origin_min_pic/16/10/29/2ac8e99273bc079e40a8dc079ca11b1f.jpg");
imgsMap.put("第二张图片.jpg", "http://bpic.588ku.com/element_origin_min_pic/16/07/13/165785fde37639b.jpg");
imgsDataMap.put("中秋", imgsMap);
Map<String, Object> imgsMap2 = new HashMap<>();
imgsMap2.put("第一张图片.jpg", "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201901%2F18%2F20190118175923_UABKt.thumb.700_0.png");
imgsMap2.put("第二张图片.jpg", "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fwww.mux5.com%2Fpicture%2F7520f61b590ff33fc408f6a4803aecd5.jpg");
imgsDataMap.put("元宵", imgsMap2);
// 下载压缩文件
downloadZipFile(imgsDataMap, fileStr, zipFileStr);
}
/**
* 下载压缩文件
*
* @param data 数据集合【key:分类名称,value:照片信息集合(key:照片名称,value:照片下载路径)】
* @param fileStr 照片存放的文件路径
* @param zipFileStr 压缩文件的路径(加后缀名)
*/
public static void downloadZipFile(Map<String, Map<String, Object>> data, String fileStr, String zipFileStr) {
try {
long timeMillis = System.currentTimeMillis();
// 创建一个file实例对象,指向文件路径(存放照片的根目录)
File rootFile = new File(System.getProperty("java.io.tmpdir") + fileStr + timeMillis);
// 创建根目录【必须实际创建文件夹,否则会在new FileOutputStream(imgFile)创建流时会报错:java.io.FileNotFoundException: C:\Users\******\temp\image1635490490475\中秋\第二张图片.jpg (系统找不到指定的路径。)】
if (!rootFile.exists()) {
// 创建新文件夹,可以多层(mkdir()创建新文件夹,只能创建一层)
rootFile.mkdirs();
}
// 将照片分类到不同目录
for (Map.Entry<String, Map<String, Object>> dataMap : data.entrySet()) {
// 指定父路径和文件路径,创建一个二级File对象【只是创建了实例对象,并没有实际创建该目录】
File secondLevelFile = new File(rootFile, dataMap.getKey());
if (!secondLevelFile.exists()) {
//这才是实际创建了
secondLevelFile.mkdirs();
}
// 下载照片到目录下
for (Map.Entry<String, Object> imgsDataMap : dataMap.getValue().entrySet()) {
//new一个URL对象
URL url = new URL(imgsDataMap.getValue().toString());
//打开链接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置请求方式为"GET"
conn.setRequestMethod("GET");
//超时响应时间为5秒
conn.setConnectTimeout(5 * 1000);
//通过输入流获取图片数据
InputStream inStream = conn.getInputStream();
//得到图片的二进制数据,以二进制封装得到数据,具有通用性
byte[] bt = readInputStream(inStream);
//new一个file对象用来保存图片,保存在指定目录下
File imgFile = new File(secondLevelFile, imgsDataMap.getKey());
//创建输出流(new的jpg文件)【这一步才真的新建了文件】
try (FileOutputStream outStream = new FileOutputStream(imgFile)) {
//将照片二进制数据写入到new的ipg文件【这一步并不会真的写入,文件是0kb】
outStream.write(bt);
//try代码块结束时会调用close方法【这一步才会把数据写入到文件进去,因为close方法包含一次flush方法】
} catch (IOException e) {
throw new IOException(e);
}
}
}
// 创建文件输出流(zip流对象)【实际创建了zip文件,0kb】
FileOutputStream fos1 = new FileOutputStream(new File(zipFileStr + DateUtils.longToDateString(timeMillis, "yyyyMMdd") + ".zip"));
// 压缩法
toZip1(rootFile, fos1, true);
// 删除文件和压缩文件
//delFolder(fileStr);
//delFolder(zipFileStr);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 压缩的递归方法
*
* @param sourceFile 源文件
* @param zos zip输出流
* @param fileName 源文件的名称
* @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
* false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
*/
private static void compress(File sourceFile, ZipOutputStream zos, String fileName, boolean keepDirStructure) throws IOException {
byte[] buf = new byte[2 * 1024];
// 判断是否是一个文件
if (sourceFile.isFile()) {
// 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
zos.putNextEntry(new ZipEntry(fileName));
// 创建文件(即某张图片)的输入流
try (FileInputStream in = new FileInputStream(sourceFile)) {
int len;
// read方法:每调用一次就从FileInputStream流中读取一个字节,并返回下一个数据字节,若已到达末尾,就返回-1。
while ((len = in.read(buf, 0, buf.length)) != -1) {
zos.write(buf, 0, len);
}
// 实际写入到了zip输出流的zip实体中,还没写到文件中【zip文件时0kb,不能打开,因为流没有关闭】
zos.closeEntry();
} catch (IOException e) {
throw new IOException(e);
}
} else {
// 源文件时目录
// 获取该目录下所有文件和目录的绝对路径
File[] listFiles = sourceFile.listFiles();
// 空目录
if (listFiles == null || listFiles.length == 0) {
// 需要保留原来的文件结构时,需要对空文件夹进行处理
if (keepDirStructure) {
// 空文件夹的处理
zos.putNextEntry(new ZipEntry(fileName + "/"));
// 没有文件,不需要文件的copy
zos.closeEntry();
}
} else {
// 非空目录
for (File file : listFiles) {
if (keepDirStructure) {
// 注意:getName()仅得到最后一层的名字,不是路径,所以要加“/”,不然所有文件都跑到压缩包根目录下了
compress(file, zos, fileName + "/" + file.getName(), true);
} else {
compress(file, zos, file.getName(), false);
}
}
}
}
}
/**
* 压缩成ZIP 方法1:保留多级目录结构
*
* @param sourceFile 照片存放路径
* @param out 压缩文件输出流
* @param keepDirStructure 是否保留原来的目录结构,true:保留目录结构;
* false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
*/
public static void toZip1(File sourceFile, OutputStream out, boolean keepDirStructure) throws IOException {
long start = System.currentTimeMillis();
// 创建压缩输出流,java7的新语法:Try-with-resources,会确保异常抛出或者try代码块结束时close流【该流必须实现AutoCloseable类】(若是java7之前的版本,则不会生效)
try (ZipOutputStream zos = new ZipOutputStream(out)) {
// 压缩
compress(sourceFile, zos, sourceFile.getName(), keepDirStructure);
long end = System.currentTimeMillis();
System.out.println("压缩完成,耗时:" + (end - start) + " ms");
} catch (IOException e) {
throw new IOException(e);
}
}
/**
* 压缩成ZIP 方法2:所有文件压缩到一个目录下
*
* @param srcFiles 需要压缩的文件列表
* @param zos 压缩文件的输出流
*/
public static void toZip2(List<File> srcFiles, ZipOutputStream zos) throws IOException {
long start = System.currentTimeMillis();
// 压缩文件的输出流
for (File srcFile : srcFiles) {
byte[] buf = new byte[2 * 1024];
zos.putNextEntry(new ZipEntry(srcFile.getName()));
try (FileInputStream in = new FileInputStream(srcFile)) {
int len;
while ((len = in.read(buf)) != -1) {
zos.write(buf, 0, len);
}
zos.closeEntry();
} catch (IOException e) {
throw new IOException(e);
}
}
long end = System.currentTimeMillis();
System.out.println("压缩完成,耗时:" + (end - start) + " ms");
}
/**
* 得到图片的二进制数据,以二进制封装得到数据,具有通用性
*
* @param inStream 输入流
*/
private static byte[] readInputStream(InputStream inStream) throws IOException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
// 创建一个Buffer字符串
byte[] buffer = new byte[1024];
// 每次读取的字符串长度,如果为-1,代表全部读取完毕
int len;
// 使用一个输入流从buffer里把数据读取出来
while ((len = inStream.read(buffer)) != -1) {
// 用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度
outStream.write(buffer, 0, len);
}
// 关闭输入流
inStream.close();
// 把outStream里的数据写入内存
return outStream.toByteArray();
}
/**
* 删除文件夹
*
* @param folderPath 文件夹完整绝对路径
*/
public static void delFolder(String folderPath) {
try {
// 删除目录下所有内容
delAllFile(folderPath);
File myFilePath = new File(folderPath);
//删除空文件夹
myFilePath.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 删除指定文件夹下所有文件
*
* @param path 文件夹完整绝对路径
*/
public static boolean delAllFile(String path) {
boolean bea = false;
File file = new File(path);
if (!file.exists()) {
return bea;
}
if (!file.isDirectory()) {
return bea;
}
//
String[] tempList = file.list();
File temp;
if (tempList != null) {
for (String var : tempList) {
// separator 代替文件或文件夹路径的斜线或反斜线,防止跨平台出现错误
if (path.endsWith(File.separator)) {
temp = new File(path + var);
} else {
temp = new File(path + File.separator + var);
}
if (temp.isFile()) {
temp.delete();
}
if (temp.isDirectory()) {
//先删除文件夹里面的文件
delAllFile(path + "/" + var);
//再删除空文件夹
delFolder(path + "/" + var);
bea = true;
}
}
}
return bea;
}
}
参考:https://blog.youkuaiyun.com/qq_43037478/article/details/98211891
============
有任何疑问或者建议欢迎随时评论交流哈~~