需求:
批量导入商品的图片;图片是用Zip包的形式;
如下代码是上传Zip的业务代码,我们在别处调用的话直接去调用productBatchUploadManager.productBatchUploadQuickly(MultipartFile multipartFile)
即可;
productUploadQuickly(MultipartFile multipartFile)
方法中分好几步去执行上传解析Zip中的图片:
uZipUtils.readZipContentByFileMutiFile(multipartFile)
去解析Zip包,并将该包解析完成的内容放在服务器的某一个地方;checkBatchUpload()
对解压完成的内容进行校验,因为你在校验完毕之前是不能对该内容进行上传或者数据库操作的,所以这一步必须进行业务操作之前进行校验;productBatchUpload();
对解压完成的内容进行上传以及数据库操作;deleteBatchUpload(UZipUtils.OUT_PUT_DIRECTORY);
删除在解析完成后存放在服务器的临时文件
当然在有一定的并发的情况下,会存在线程1在执行checkBatchUpload
,线程2在执行deleteBatchUpload
,这样就会出现问题,我们可以在productUploadQuickly
方法上加上synchronized,确保只有一个线程去执行该代码;
/**
* @Author:朱国庆
* @Date:2020/7/29 10:33
* @Desription: qidian-server-distribution
* @Version: 1.0
*/
@Service
public class ProductBatchUploadManagerImpl implements ProductBatchUploadManager {
@Autowired
private UZipUtils uZipUtils;
@Autowired
private UploadHandler uploadHandler;
@Autowired
private ProductDomainMapper productDomainMapper;
@Autowired
private GoodsApiMapper goodsApiMapper;
@Override
public void checkBatchUpload() {
File file = new File(UZipUtils.OUT_PUT_DIRECTORY);
File[] fileDirectories = file.listFiles();
for (File fileDirectory : fileDirectories) {
if (fileDirectory.isDirectory()) {
String spuName = fileDirectory.getName();
//对名称进行切割.将文件名做为spuId
//spuName = spuName.substring(0, spuName.lastIndexOf("/"));
Long spuNameId;
try {
spuNameId = Long.parseLong(spuName);
} catch (NumberFormatException e) {
throw new RuntimeException(spuName + "文件夹名称格式不合法,必须是spuId");
}
//去数据库中查询,如果spuId不存在,那么就抛出异常
ProdSpuDO prodSpuDO = productDomainMapper.queryProdSpuDOById(spuNameId);
if (Objects.isNull(prodSpuDO)) {
throw new RuntimeException(spuName + "文件夹找不到对应的商品spu");
}
File[] files = fileDirectory.listFiles();
//该集合用来收集主图
ArrayList<File> fileMain = new ArrayList<>();
for (File fileToOSS : files) {
//过滤掉隐藏文件
if (fileToOSS.getName().startsWith(".")) {
continue;
}
if (fileToOSS.isFile()) {
String name = fileToOSS.getName();
//String spuNameTemp = name.substring(0, name.lastIndexOf("/"));
//对切割完的数据将后缀名去掉
String spuFileName = name.substring(0, name.lastIndexOf("."));
Long spuFileNameId;
String suffixName = name.substring(name.lastIndexOf("."));
if (!(suffixName.equalsIgnoreCase(".JPG")
|| suffixName.equalsIgnoreCase(".JPEG")
|| suffixName.equalsIgnoreCase(".PNG"))) {
throw new RuntimeException(name + "图片格式格式不和法,必须是是JPG,JPEG,PNG格式");
}
if (spuFileName.contains(spuName)) {
//主图
try {
try {
spuFileName = spuFileName.substring(0, spuFileName.lastIndexOf("("));
} catch (Exception e) {
spuFileNameId = Long.parseLong(spuFileName);
}
} catch (NumberFormatException e) {
throw new RuntimeException(spuFileName + "图片名称格式不合法,必须是spuId");
}
fileMain.add(fileToOSS);
} else {
//sku图片
try {
spuFileNameId = Long.parseLong(spuFileName);
} catch (NumberFormatException e) {
throw new RuntimeException(spuFileName + "图片名称格式不合法,必须是skuId");
}
ProdSkuDO prodSkuDO = goodsApiMapper.queryProdSkuDOBySkuId(spuFileNameId);
if (Objects.isNull(prodSkuDO)) {
throw new RuntimeException(spuFileName + "图片找不到对应的商品sku");
}
}
}
}
if (fileMain.size() > 8) {
throw new RuntimeException("商品主图最多为8张");
}
}
}
}
@Override
@Transactional
public void productBatchUpload() {
File file = new File(UZipUtils.OUT_PUT_DIRECTORY);
File[] fileDirectories = file.listFiles();
for (File fileDirectory : fileDirectories) {
//如果判断该文件是文件夹,那么就表示
if (fileDirectory.isDirectory()) {
String spuName = fileDirectory.getName();
//对名称进行切割.将文件名做为spu
//spuName = spuName.substring(0, spuName.lastIndexOf("/"));
File[] files = fileDirectory.listFiles();
//首先删除所有的主图
productDomainMapper.deleteProdMedia(Long.parseLong(spuName));
for (File fileToOSS : files) {
//过滤掉隐藏文件
if (fileToOSS.getName().startsWith(".")) {
continue;
}
if (fileToOSS.isFile()) {
String spuNameTemp = fileToOSS.getName();
//String spuNameTemp = name.substring(0, name.lastIndexOf("/"));
//对切割完的数据将后缀名去掉
String spuFileName = spuNameTemp.substring(0, spuNameTemp.lastIndexOf("."));
UploadResult uploadResult = uploadHandler.upload("GOODS", fileToOSS.getName(), fileToOSS);
List<ItemResult> resultDetail = uploadResult.getResultDetail();
if (resultDetail.size() >= 1) {
//如果上传成功,会返回响应的数据
if ("success".equalsIgnoreCase(resultDetail.get(0).getResult())) {
String url = resultDetail.get(0).getUrl();
if (spuFileName.contains(spuName)) {
//按照规则,如果说spuFileName包含了spuName,那么该文件一定是商品主图
ProdMediaDO prodMediaDO = new ProdMediaDO();
prodMediaDO.setSpuId(Long.parseLong(spuName));
//资源类型为图片
prodMediaDO.setType(0);
prodMediaDO.setPictureUrl(url);
//再新增主图
productDomainMapper.saveProdMedia(prodMediaDO);
} else {
//否则一定是sku图
productDomainMapper.updatePicBySkuId(Long.parseLong(spuFileName), url);
}
}
}
}
}
}
}
}
@Override
public void deleteBatchUpload(String path) {
File file = new File(path);
if (file.isFile()) {
file.delete();
} else {
File[] files = file.listFiles();
if (files == null) {
file.delete();
} else {
for (int i = 0; i < files.length; i++) {
deleteBatchUpload(files[i].getAbsolutePath());
}
file.delete();
}
}
}
@Override
public synchronized void productBatchUploadQuickly(MultipartFile multipartFile) {
try {
try {
uZipUtils.readZipContentByFileMutiFile(multipartFile);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("ZIP解压失败", e);
}
checkBatchUpload();
productBatchUpload();
} catch (RuntimeException e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
deleteBatchUpload(UZipUtils.OUT_PUT_DIRECTORY);
}
}
}
下面代码是Zip解析工具类
/**
* @Author:朱国庆
* @Date:2020/7/28 12:27
* @Desription: qidian-server-distribution
* @Version: 1.0
*/
@Component
public class UZipUtils {
private static byte[] doc = null;
//todo 测试之后需要修改
public static final String OUT_PUT_DIRECTORY = "/Users/fechinchu/home/htd/qidian/tmp/";
/**
* 目前上传的图片只支持JPG,JPEG,PNG 大小不超过2M,图片建议800*800像素,商品主图最多为8张图片
*
* @param inputStream
* @throws Exception
*/
public static void readZipContentByInputStream(InputStream inputStream) throws IOException {
try {
//这里filename是文件名,如xxx.zip
ZipInputStream zipis = new ZipInputStream(inputStream);
ZipEntry fentry = null;
while ((fentry = zipis.getNextEntry()) != null) {
//fentry逐个读取zip中的条目,第一个读取的名称为文件夹。因此会创建一个File对象,File对象接收的参数为地址
//然后就会用exists,判断该参数所指定的路径的文件或者目录是否存在
//如果不存在,则构建一个文件夹;若存在,跳过
//如果读到一个zip,也继续创建一个文件夹,然后继续读zip里面的文件,如txt
if (fentry.isDirectory()) {
File dir = new File(OUT_PUT_DIRECTORY + fentry.getName());
if (!dir.exists()) {
dir.mkdirs();
}
} else {
//fname是文件名,fileoutputstream与该文件名关联
String fname = new String(OUT_PUT_DIRECTORY + fentry.getName());
try {
//新建一个out,指向fname,fname是输出地址
FileOutputStream out = new FileOutputStream(fname);
doc = new byte[512];
int n;
//若没有读到,即读取到末尾,则返回-1
while ((n = zipis.read(doc, 0, 512)) != -1) {
//这就把读取到的n个字节全部都写入到指定路径了
out.write(doc, 0, n);
}
out.close();
out = null;
doc = null;
} catch (Exception ex) {
System.out.println("there is a problem");
}
}
}
zipis.close();
} catch (IOException ioex) {
System.out.println("io错误:" + ioex);
}
System.out.println("finished!");
}
public void readZipContentByFileMutiFile(MultipartFile multipartFile) throws Exception {
byte[] bytes = multipartFile.getBytes();
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
readZipContentByInputStream(inputStream);
}
}
如上就是所有关于图片Zip批量上传的所有内容,还有什么样的问题或者说还有什么需要我们去改进的,我们能不能做成多线程的方式,节省上传和处理的时间。欢迎大家一起讨论。