使用者上傳頭像 → 前端裁切預覽 → 後端按座標裁切縮放 → 回存資料庫 → 整合 Lodop 列印預覽。技術棧:Spring MVC、jQuery imgAreaSelect 裁切插件、ajaxFileUpload 非同步上傳、Java ImageIO 處理圖片。
<link rel="stylesheet" href="${frontRes}/imgareaselect/css/imgareaselect-animated.css" />
<script src="${frontRes}/imgareaselect/jquery-1.4.2.min.js"></script>
<script src="${frontRes}/imgareaselect/jquery.imgareaselect.pack.js"></script>
<script src="${frontRes}/imgareaselect/ajaxfileupload.js"></script>
function addFile(obj) {
$.ajaxFileUpload({
url: '${huluUrl}/trainer/upload/ajaxUpload',
secureuri: false,
fileElementId: 'photoFile',
dataType: 'json',
success: function(data) {
var info = eval("(" + data + ")");
var arr = info.data;
$("#img").attr("src", "/images/" + arr.photo); // 更新大圖
initCropper(arr.photo); // 初始化裁切區
},
error: function() { alert('上傳失敗'); }
});
}
function initCropper(photoName) {
var $photo = $('#photo').attr('src', '/images/' + photoName);
$photo.one('load', function() {
var imgW = $photo.width(), imgH = $photo.height();
var size = Math.min(imgW, imgH); // 正方形裁切區
var ias = $photo.imgAreaSelect({
aspectRatio: '1:1', handles: true, fadeSpeed: 200,
onSelectChange: preview, instance: true
});
ias.setSelection(0, 0, size-1, size-1);
ias.setOptions({show: true}); ias.update();
// 同步座標到隱藏欄位供後端使用
$('#x1s').val(0); $('#y1s').val(0);
$('#x2s').val(size-1); $('#y2s').val(size-1);
$('#imgx').val(imgW); $('#imgy').val(imgH);
});
}
function preview(img, sel) {
if (!sel.width || !sel.height) return;
var scaleX = 100 / sel.width, scaleY = 100 / sel.height;
var imgW = parseInt($('#photo').css('width')), imgH = parseInt($('#photo').css('height'));
$('#preview img').css({
width: Math.round(scaleX * imgW), height: Math.round(scaleY * imgH),
marginLeft: -Math.round(scaleX * sel.x1), marginTop: -Math.round(scaleY * sel.y1)
});
// 回填座標
$('#x1').val(sel.x1); $('#y1').val(sel.y1);
$('#x2').val(sel.x2); $('#y2').val(sel.y2);
$('#w').val(sel.width); $('#h').val(sel.height);
$('#x1s').val(sel.x1); $('#y1s').val(sel.y1);
$('#x2s').val(sel.x2); $('#y2s').val(sel.y2);
}
@RequestMapping("/ajaxUpload")
@ResponseBody
public AjaxResult ajaxUpload(MultipartHttpServletRequest request) {
MultipartFile file = request.getFile("photoFile");
if (file.isEmpty()) return AjaxResult.error();
String ext = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
String saveName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ext;
File dest = new File("E:/rudongImage/photo/", saveName);
FileCopyUtils.copy(file.getBytes(), dest);
Trainer trainer = new Trainer();
trainer.setPhoto("photo/" + saveName);
trainer.setUserId(FrontShiroUtil.getUserId());
trainerDbSvc.updateTrainerPhoto(trainer); // 先存暫存路徑
return AjaxResult.success(trainer);
}
@RequestMapping(value = "uploadPhotoTest", method = RequestMethod.POST)
public ModelAndView uploadPhotoTest(
@RequestParam("x1s") int x1, @RequestParam("y1s") int y1,
@RequestParam("x2s") int x2, @RequestParam("y2s") int y2,
@RequestParam("imgx") int imgW, @RequestParam("imgy") int imgH) {
Trainer trainer = trainerRepo.findOne(FrontShiroUtil.getUserId());
File src = new File("E:/rudongImage/" + trainer.getPhoto());
BufferedImage bi = ImageIO.read(src);
int srcW = bi.getWidth(), srcH = bi.getHeight();
// 前端預覽圖可能被縮放,需按比例換算回原圖座標
int base = Math.min(srcW, srcH); // 原圖裁切基準邊
int previewBase = Math.min(imgW, imgH); // 前端預覽基準邊
int sx = x1 * base / previewBase;
int sy = y1 * base / previewBase;
int ex = x2 * base / previewBase;
int ey = y2 * base / previewBase;
// 呼叫工具類裁切
ImageUtil util = new ImageUtil();
String newName = util.cutImage(src, "E:/rudongImage/photo/", sx, sy, ex - sx, ey - sy);
trainer.setPhoto("photo/" + newName);
trainerDbSvc.updateTrainerPhoto(trainer);
return new ModelAndView("redirect:/trainer/upload/toUploadPhoto");
}
public void cutImage(File src, String destDir, int x, int y, int w, int h) {
FileInputStream fis = new FileInputStream(src);
ImageInputStream iis = ImageIO.createImageInputStream(fis);
ImageReader reader = ImageIO.getImageReadersBySuffix(getSuffix(src)).next();
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
param.setSourceRegion(new Rectangle(x, y, w, h)); // 關鍵:只讀取指定區域
BufferedImage bi = reader.read(0, param);
String outName = "cut_" + System.currentTimeMillis() + "_" + src.getName();
ImageIO.write(bi, getSuffix(src), new File(destDir, outName));
// 關閉流...
return outName;
}
<object id="LODOP_OB" classid="clsid:2105C259-1E0C-4534-8141-A753534CB4CA" width=0 height=0>
<embed id="LODOP_EM" type="application/x-print-lodop" width=0 height=0></embed>
</object>
<script src="/js/Lodop/LodopFuncs.js"></script>
function PrintURL() {
var LODOP = getLodop(document.getElementById('LODOP_OB'), document.getElementById('LODOP_EM'));
LODOP.PRINT_INIT("");
LODOP.ADD_PRINT_HTM(45, 55, 770, 1004, document.getElementById("print").innerHTML);
LODOP.PREVIEW(); // 或 PRINT()
}
Tomcat 需配置虛擬目錄映射圖片儲存路徑:server.xml 加入 <Context docBase="G:\Image" path="/images" />。
本方案完整串聯:非同步上傳 → 前端裁切預覽 → 座標比例換算 → 後端高效裁切(ImageReadParam 只讀取目標區域,記憶體友善) → 列印輸出。適合需求「上傳即裁切、列印輸出」的實名認證、證照上傳等業務場景。