10萬+IT人都在關注的圖檔批量壓縮上傳方案(完整案例+代碼)
背景需求:為了用戶端通路圖檔資源時,加載圖檔更流暢,體驗更好,通常不會直接用原圖路徑,需要根據不同的場景顯示不同規格的縮略圖,根據商品關鍵屬性,能夠擷取到圖檔不同尺寸規格的圖檔路徑,并且能根據不同縮略圖直覺看到商品的關鍵屬性,需要寫一個Java小工具把本地磁盤中的圖檔資源一鍵上傳至分布式FastDFS檔案伺服器,并把圖檔資訊存入本地資料庫,PC端或者用戶端查詢商品時,就可以根據商品的業務屬性。比如根據productId就能把商品相關的不同尺寸規格的圖檔都擷取到,頁面渲染圖檔資源時,不同的場景,直接通過檔案伺服器的IP+存儲路徑,可以線上預覽。示例:商品id為1001的主圖原圖1001.jpg,大小為800×800(px),在本案例中解析為1001-50×50.jpg,1001-100×100.jpg,1001-200×200.jpg,1001-400×400.jpg,解析後連同原圖就是5種尺寸規格的圖檔。前端就能直覺的根據螢幕大小,業務場景等因素使用不同的圖檔。
實作思路:先把本地磁盤目錄中的所有圖檔資源通過IO流讀出來,讀到記憶體中,然後對圖檔的名稱根據定義好的業務規則解析,生成不同的圖檔名,然後對原圖進行不同規格的解析壓縮處理,以及圖檔資源的上傳和圖檔資訊的批量儲存至資料庫。
常用的壓縮方案有下面2種:
方案一:對原圖進行按照指定存儲空間的壓縮,比如原圖100Kb,壓縮至10Kb
方案二:對原圖進行指定寬高大小的壓縮,比如原圖800*800,壓縮至100*100
準備工作:封裝一個檔案流操作的通過工具類,如下:
package com.demo.utils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.tomcat.util.codec.binary.Base64;
/**
* 建立時間:2019年3月13日 下午9:02:32
* 項目名稱:shsc-batchUpload-server
* 類說明:檔案流工具類
* @author guobinhui
* @since JDK 1.8.0_51
*/
public class FileUtils {
/*
* 讀取本地實體磁盤目錄裡的所有檔案資源到程式記憶體
*/
public static List<File> readFiles(String fileDir) {
File dirPath = new File(fileDir);
//用listFiles()獲得子目錄和檔案
File[] files = dirPath.listFiles();
List<File> list1 = new ArrayList<File>();
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (!file.isDirectory()) {
list1.add(files[i]);
}
}
System.out.println("目錄圖檔數量為:"+list1.size());
return list1;
}
/*
* File檔案流轉為Base64的字元串流
* 注意:通過前端頁面上傳圖檔時,用 MultipartFile檔案流可以接收圖檔并上傳,MultipartFile流有很豐富的方法
* 本案例通過背景小工具上傳,需要把圖檔資源的檔案流轉為Base64格式的流才可以上傳
*/
public static String getBase64(File file) {
FileInputStream fis = null;
String base64String = null;
try {
fis = new FileInputStream(file);
byte[] buff = new byte[fis.available()];
fis.read(buff);
base64String = Base64.encodeBase64String(buff);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
if(fis != null){
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return base64String;
}
/**
* 将File檔案流轉為位元組數組
* @param file
* @return
*/
public static byte[] getByte(File file){
byte[] bytes = null;
try {
FileInputStream fis = new FileInputStream(file);
bytes = new byte[fis.available()];
fis.read(bytes);
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bytes;
}
/**
* 将位元組輸出流寫到指定檔案
* @param os
* @param file
*/
public static void writeFile(ByteArrayOutputStream os, File file){
FileOutputStream fos = null;
try {
byte[] bytes = os.toByteArray();
if (file.exists()) {
file.delete();
}
fos = new FileOutputStream(file);
fos.write(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
封裝一個壓縮圖檔處理類
package com.demo.mapper.entity;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.imageio.ImageIO;
/**
* 建立時間:2019年3月13日 下午3:35:05
* 項目名稱:shsc-batchUpload-server
* 類說明:圖檔壓縮處理類
* @author guobinhui
* @since JDK 1.8.0_51
*/
public class ImgCompress {
private Image img;
private int width;
private int height;
/**
* 構造函數
*/
public ImgCompress(String filePath) throws IOException {
File file = new File(filePath);// 讀入檔案
img = ImageIO.read(file); // 構造Image對象
width = img.getWidth(null); // 得到源圖寬
height = img.getHeight(null); // 得到源圖長
}
public Image getImg() {
return img;
}
public void setImg(Image img) {
this.img = img;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public void reSize(int w, int h,File file,String dir) throws IOException {
// SCALE_SMOOTH 的縮略算法 生成縮略圖檔的平滑度的 優先級比速度高 生成的圖檔品質比較好,但是速度慢
BufferedImage tag = new BufferedImage(50,50,BufferedImage.TYPE_INT_RGB );
Image img = ImageIO.read(file);
Image image = img.getScaledInstance(w, h, Image.SCALE_SMOOTH);
tag.getGraphics().drawImage(image,50, 50, null); // 繪制縮小後的圖
// 将輸入檔案轉換為位元組數組
byte[] bytes = FileUtils.getByte(file);
// 構造輸入輸出位元組流
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
ByteArrayOutputStream os = new ByteArrayOutputStream();
double rate = w/800;//縮放比率
try {
// 處理圖檔
zoomImage(is,os,rate);
} catch (Exception e) {
e.printStackTrace();
}
// 将位元組輸出流寫入檔案
FileUtils.writeFile(os,new File(dir+"/"+file.getName()));
}
public void zoomImage(InputStream is, OutputStream os,double Rate) throws Exception {
BufferedImage bufImg = ImageIO.read(is);
AffineTransformOp ato = new AffineTransformOp(AffineTransform.getScaleInstance(Rate,Rate), null);
BufferedImage bufferedImage = ato.filter(bufImg, null);
ImageIO.write(bufferedImage, "jpg", os);
}
}
方案一具體實作過程:
package com.demo.controller;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.imageio.ImageIO;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.demo.mapper.entity.AttachmentModel;
import com.demo.mapper.entity.ImgCompress;
import com.demo.mapper.entity.ProductPic;
import com.demo.service.IFileService;
import com.demo.utils.FileUtils;
import com.shsc.framework.common.ResultInfo;
/**
* 建立時間:2019年3月8日 下午3:03:56
* 項目名稱:shsc-batchUpload-server
* 類說明:圖檔批量壓縮上傳
* @author guobinhui
* @since JDK 1.8.0_51
*/
@RestController
@RequestMapping(value="/file")
public class FileController {
@Autowired
private IFileService fileServiceImpl;
@RequestMapping("/test")
@ResponseBody
public String test() {
//原始圖檔目錄
String originalFileDir = "D:/pics/pic1";
List <File> originalFileList = readFiles(originalFileDir);
Iterator<File> it = originalFileList.iterator();
//壓縮後的縮略圖目錄
String thumbnailDir = "D:/uploadBaseDir/productPic/20190313/thumbnail";
long startWrite = System.currentTimeMillis();
while(it.hasNext()){
File file = (File)it.next();
try {
ImgCompress img = new ImgCompress(file.getPath());
img.reSize(50, 50, file, thumbnailDir);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "上傳失敗!";
}
}
long endWrite = System.currentTimeMillis();
System.out.println("批量上傳檔案共計耗時:" +(endWrite-startWrite)/1000+"秒" );
return "<h1 style='color:red;'>批量上傳檔案成功,非常棒,壓縮上傳檔案總數量為:"+num+",共計耗時"+(endWrite-startWrite)/1000+"秒</h1>";
}
}
最後在浏覽器上通路該接口或者把該接口放在main方法裡run,效果如下:

方案二具體實作過程:
@RequestMapping("/upload")
@ResponseBody
public String upload(){
//win環境原始檔案目錄
String originalFileDir = "D:/pics/pic1";
System.out.println("讀磁盤檔案開始");
long startRead = System.currentTimeMillis();
List <File> originalFileList = readFiles(originalFileDir);
long endRead = System.currentTimeMillis();
System.out.println("讀磁盤檔案結束");
System.out.println("讀取磁盤檔案共計耗時:" +(endRead-startRead)+"毫秒" );
Iterator<File> it = originalFileList.iterator();
System.out.println("壓縮拷貝檔案開始");
long startWrite = System.currentTimeMillis();
// Integer size = 500;//每500個圖檔批量插入一次
// Integer i = 0;
String productNumber = null;
String thumbnailDir = "D:/uploadBaseDir/productPic/20190313/thumbnail";
String base64 = null;
String new50PicName = "";
String new100PicName = "";
String new200PicName = "";
String new400PicName = "";
List <ProductPic> picList = new ArrayList<ProductPic>();
int picType;
List <Integer> sizeList = new ArrayList<Integer>();
sizeList.add(0,50);
sizeList.add(1,100);
sizeList.add(2,200);
sizeList.add(3,400);
while(it.hasNext()){
File file = (File)it.next();
System.out.println("原始檔案路徑為:"+file.getPath());
String originalFileName= file.getName();
String prefixName = originalFileName.substring(0,originalFileName.lastIndexOf("."));
String ext = originalFileName.substring(originalFileName.lastIndexOf("."));
byte[] buff = FileUtils.getByte(file);
ByteArrayInputStream is = new ByteArrayInputStream(buff);
ByteArrayOutputStream os = null;
BufferedImage BI = null;
base64 = getBase64(file);
ResultInfo<?> r = fileServiceImpl.uploadBase64(base64,originalFileName);
AttachmentModel att = (AttachmentModel)r.getData();
if(originalFileName.indexOf('-') == -1) {
picType = 1;
productNumber = prefixName;
}else {
picType = 2;
productNumber = originalFileName.substring(0,originalFileName.lastIndexOf("-"));
}
if(r.isSuccess()) {
ProductPic pic = new ProductPic();
BeanUtils.copyProperties(att, pic);
pic.getPicName();
pic.setProductId(productNumber);
pic.setPicType(picType);
picList.add(pic);
}
if(originalFileName.indexOf('-') == -1) {//不帶'-'的是商品主圖
productNumber = prefixName;
new50PicName = productNumber+'-'+ "50×50"+ext;
new100PicName = productNumber+'-'+ "100×100"+ext;
new200PicName = productNumber+'-'+ "200×200"+ext;
new400PicName = productNumber+'-'+ "400×400"+ext;
}else {
productNumber = originalFileName.substring(0,originalFileName.lastIndexOf("-"));
new50PicName = originalFileName.substring(0,originalFileName.lastIndexOf("."))+'-'+ "50×50"+ext;
new100PicName = originalFileName.substring(0,originalFileName.lastIndexOf("."))+'-'+ "100×100"+ext;
new200PicName = originalFileName.substring(0,originalFileName.lastIndexOf("."))+'-'+ "200×200"+ext;
new400PicName = originalFileName.substring(0,originalFileName.lastIndexOf("."))+'-'+ "400×400"+ext;
}
try {
File f = null;
BI = ImageIO.read(is);
for (int i = 0; i < sizeList.size(); i++) {
os = new ByteArrayOutputStream();
Image image = BI.getScaledInstance(sizeList.get(i),sizeList.get(i), Image.SCALE_SMOOTH);
BufferedImage tag = new BufferedImage(sizeList.get(i),sizeList.get(i),BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.setColor(Color.RED);
g.drawImage(image, 0, 0, null); //繪制處理後的圖
g.dispose();
ImageIO.write(tag, "jpg", os);
if(sizeList.get(i) == 50) {
FileUtils.writeFile(os,new File(thumbnailDir+"/"+new50PicName));
f = new File(thumbnailDir+"/"+new50PicName);
}else if(sizeList.get(i) == 100) {
FileUtils.writeFile(os,new File(thumbnailDir+"/"+new100PicName));
f = new File(thumbnailDir+"/"+new100PicName);
}else if(sizeList.get(i) == 200) {
FileUtils.writeFile(os,new File(thumbnailDir+"/"+new200PicName));
f = new File(thumbnailDir+"/"+new200PicName);
}else if(sizeList.get(i) == 400) {
FileUtils.writeFile(os,new File(thumbnailDir+"/"+new400PicName));
f = new File(thumbnailDir+"/"+new400PicName);
}
base64 = getBase64(f);
ResultInfo<?> rr = fileServiceImpl.uploadBase64(base64,f.getName());
if(rr.isSuccess()) {
AttachmentModel atta = (AttachmentModel)rr.getData();
if(atta.getPicName().indexOf('-') == -1) {//不帶'-'的是商品主圖
picType = 1;
}else if(atta.getPicName().indexOf("-1.") != -1
|| atta.getPicName().indexOf("-2.") != -1
|| atta.getPicName().indexOf("-3.") != -1
|| atta.getPicName().indexOf("-4.") != -1) {
picType = 2;
}else if((atta.getPicName().indexOf("-1-") == -1
||atta.getPicName().indexOf("-2-") == -1
||atta.getPicName().indexOf("-3-") == -1
||atta.getPicName().indexOf("-4-") == -1)
&& atta.getPicName().indexOf("-") != -1) {
picType = 3;
}else {
picType = 4;
}
ProductPic pic = new ProductPic();
BeanUtils.copyProperties(atta, pic);
pic.getPicName();
pic.setProductId(productNumber);
pic.setPicType(picType);
picList.add(pic);
}
}
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
int num = fileServiceImpl.insertPics(picList);
if(num > 0) {
long endWrite = System.currentTimeMillis();
System.out.println("批量上傳檔案共計耗時:" +(endWrite-startWrite)/1000+"秒" );
return "<h1 style='color:red;'>批量上傳檔案成功,非常棒,壓縮上傳檔案總數量為:"+num+",共計耗時"+(endWrite-startWrite)/1000+"秒</h1>";
}
return "批量上傳檔案失敗!";
}
更多JavaEE資料請關注下面公衆号,歡迎廣大開發者朋友一起交流。筆者電話(微信):18629374628