Java Servlet 圖片處理專案實作 (二)
自定切割壓縮圖片,壓縮為 Zip 回傳給使用者。
自定切割壓縮圖片 (ZipImage)
可接受使用者輸入要切割的多組圖片大小, 接受兩組參數:
resize輸入格式預期為 ["60x60", "120x200"] 這種格式。 若是沒有輸入 resize 參數,系統預設幫使用者切割為 ["100x100", "250x250", "600x600"] 三個 size
imageFile: 圖片檔案
處理完成後回傳一個 zip 檔案給使用者,zip 檔名格式為 {圖片名稱}{寬}x{高}.{format}
ex.
# zip-image.zip
./image100x100.jpg
./image250x250.jpg
./image600x600.jpg
ok! 咱們開始吧!
簡易 HTML 表單 ZipImage.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定切割壓縮圖片</title>
</head>
<body>
<form
action="zip-image"
method="POST"
enctype="multipart/form-data"
>
<p>選擇圖片</p>
<input type="file" name="imageFile">
<p>指定尺寸</p>
<input type="text" name="resize">
<p>指定尺寸</p>
<input type="text" name="resize">
<p>指定尺寸</p>
<input type="text" name="resize">
<button type="submit">送出</button>
</form>
</body>
</html>
圖片寬高資訊物件 ResizeInfo.java
/**
* 要輸出的圖片寬高資訊
*/
class ResizeInfo {
// 要輸出的寬
public int m_width;
// 要輸出的高
public int m_height;
/**
* @param sSize {String} - 字串通常是 60x60 120x200 這種格式
*/
ResizeInfo(String sSize) {
this(sSize.split("x"));
}
private ResizeInfo(String[] spSize) {
m_width = Integer.parseInt(spSize[0]);
m_height = Integer.parseInt(spSize[1]);
}
@Override
public String toString() {
return m_width + "x" + m_height;
}
}
因為只有 ResizeInfo.java 稍微有點不好用, 我額外做了一個工廠方法物件來幫忙產生 ResizeInfo 物件 (因為不是這次的主題,稍微看一看就好!不用太執著在這上面) ResizeInfoFactory.java
/**
* @author wayne on 2020/4/10
*/
public class ResizeInfoFactory {
/**
* @param sizeArr - 接收到使用者指定的圖片尺寸陣列
* @return - 沒有的話回傳 Optional.empty()
*/
public static Optional<List<ResizeInfo>> getResizeInfo(String[] sizeArr) {
/**
* 例外判斷
*/
if (sizeArr == null || sizeArr[0].isEmpty()) {
return Optional.empty();
}
List<String> sizeList = Arrays.asList(sizeArr); // 字串轉為 List 結構
List<ResizeInfo> resizeInfoList = getResizeInfo$(sizeList);
return Optional.of(resizeInfoList);
}
public static List<ResizeInfo> defaultResizeInfo() {
List<String> defaultSizeList = Arrays.asList("100x100", "250x250", "600x600");
return getResizeInfo$(defaultSizeList);
}
/**
* 物件內部的轉換方法,不公開給外部使用
* $ 符號代表直接取得,不處理例外
* @return 處理陣列字串轉為陣列 ResizeInfo 物件
*/
private static List<ResizeInfo> getResizeInfo$(List<String> sizeList) {
return sizeList.stream()
.map(sizeItem -> new ResizeInfo(sizeItem))
.collect(Collectors.toList())
;
}
}
Servlet 物件 ZipImage.java
@MultipartConfig() // 這個是為了要使用 getPart() 方法一定要加的哦!
@WebServlet(
name = "ZipImage",
urlPatterns = {"/zip-image"}, // servlet entry url
loadOnStartup = 1
)
public class ZipImage extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 設定回應格式
resp.setContentType("application/zip");
// Servlet 輸出流,用於輸出檔案給用戶
ServletOutputStream out = resp.getOutputStream();
ZipOutputStream zipOut = new ZipOutputStream(out); // 用檔案輸出流建立出 Zip 輸出流
// 處理使用者傳入想要壓縮的 size
String[] sizeArr = req.getParameterMap().get("resize"); // {接收使用者指定的圖片尺寸},收到的會是陣列格式
List<ResizeInfo> resizeInfoList = ResizeInfoFactory
.getResizeInfo(sizeArr)
.orElse(ResizeInfoFactory.defaultResizeInfo()) // 如果使用者未指定尺寸的話,回傳預設的切割 size
;
Part imagePart = req.getPart("imageFile"); // 取得使用者上傳的圖片檔案
String fileName = imagePart.getSubmittedFileName(); // 使用者上傳的檔名
String formatName = fileName.substring(fileName.lastIndexOf(".") + 1); // 副檔名(拿原圖的副檔名就好)
/**
* 把所有 ResizeInfo 物件迭代一次
*/
resizeInfoList.stream().forEach(resizeInfo -> {
ZipEntry zipEntry = new ZipEntry(fileName + resizeInfo.toString() + "." + formatName);
try {
BufferedImage bufferedImage = ImageIO.read(imagePart.getInputStream());
// 建立一個空白的 BufferedImage 物件,
// 寬度、高度 為使用者輸入的
// 輸出類型為使用者上傳的圖片類型
BufferedImage exportBFImage = new BufferedImage(resizeInfo.m_width, resizeInfo.m_height, bufferedImage.getType());
// 用剛才建立的空白 BufferedImage 物件來建立畫布
Graphics2D g2d = exportBFImage.createGraphics();
g2d.drawImage(
bufferedImage, // 把我們讀入的圖片畫上去
0, // x軸起始點
0, // y軸起始點
resizeInfo.m_width, // 要畫上去的寬度
resizeInfo.m_height, // 要畫上去的長度
null
);
g2d.dispose(); // g2d 就不再接受被寫入內容
zipOut.putNextEntry(zipEntry);
ImageIO.write(exportBFImage, formatName, zipOut);
} catch (IOException e) {
e.printStackTrace();
}
});
// 關閉輸出流
zipOut.close();
out.close();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// "/" 開頭帶表從我們 Smart Tomcat 定義的 Deployment Directory 開始
// 也就是 webapp 那個目錄找檔案
RequestDispatcher view = req.getRequestDispatcher("/ZipImage.html");
// 轉回去給使用者
view.forward(req, resp);
}
}
這次專案的程式碼一樣上 github 了! 歡迎拉下來看看!