Jcrop外掛+Canvas實現圖片上傳預覽+圖片裁剪上傳
阿新 • • 發佈:2019-01-03
前言
想實現一個功能:使用者點選上傳按鈕,選擇圖片後。圖片顯示在一個彈出框上,並可以對圖片進行裁剪。裁剪後的圖片顯示在頁面上。提交表單即可上傳圖片。
遇到問題
- 瀏覽器的安全設定不讓使用者獲取上傳的圖片路徑,實際獲取的是c:\fakepath\a.jpg(假設圖片是c盤下的a.jpg)
- ajax如何非同步上傳圖片?
- Canvas畫裁剪的圖片時長寬不一樣(本來想畫100x100的圖片,結果在畫布上顯示100x50)
- Canvas上的圖片怎麼上傳?
問題解決
- 選擇圖片後,觸發input的onchange事件:傳送非同步請求將圖片上傳,返回圖片的名稱,然後彈框,將圖片顯示出來。
- 將表單封裝為FormData型別,ajax進行一些設定即可,具體百度有說。
- 設定Canvas寬高我用的是style:width:100px;height:100px;,後來直接寫Canvas的屬性width=100px height=100px,問題就解決了。具體原因百度有說。
- 獲取Canvas的圖片流(實際上就是一段字串),表單提交圖片流。後臺用Base64解析圖片流成為byte陣列,然後根據byte陣列建立輸入流,有了輸入流,就能夠寫出一張圖片了。
部分程式碼
<!-- 圖片上傳,openPhotoCut的功能是非同步上傳圖片,顯示圖片並裁剪 -->
<form id="uploadForm" class="form-horizontal" role="form" enctype="multipart/form-data">
<div class="form-group namediv">
<label class="col-sm-2" for="photo" style="font-size: 15px;"><span style="color:red;"> </span>菜品圖片</label>
<div class="col-sm-6">
<input type="file" class="photo" id="photo" name="photo" onchange="openPhotoCut();" />
</div>
</div>
</form>
// 開啟圖片裁剪框
function openPhotoCut() {
// ajax上傳圖片的關鍵
var formData = new FormData($("#uploadForm"));
var fileName; // 上傳檔名
// 在表單裡面新增上傳的圖片
formData.append("photo", $(".photo").get(0).files[0]);
// 上傳未裁剪的圖片
$.ajax({
type: "POST",
url: "${pageContext.request.contextPath }/business/product/getFileLocationAjax",
async: false,
data: formData,
// 以下兩行不寫可能會報錯
contentType: false,
processData: false,
success: function(result) {
// 得到圖片名稱
fileName = result;
alert(fileName);
}
});
// 這裡使用的是layer彈框外掛,彈出圖片裁剪框,顯示上傳的圖片,並進行裁剪
layer.open({
title: '圖片裁剪',
type: 1,
skin: 'layui-layer-rim', //加上邊框
area: ['600px', '500px'], //寬高
// 這裡純粹是html內容,cutAndShow的功能是把剛才上傳的圖片刪除,然後把裁剪的圖片顯示在Canvas上,並儲存圖片流在input中。
content: '<img id="prephoto" src="${pageContext.request.contextPath }/uploads/' + fileName + '"/>' +
'<button class="btn btn-primary" style="margin: 20px;" onclick="cutAndShow(\'' + fileName + '\');">裁剪</button>' +
'x軸:<input type="text" readonly id="x" name="x" value="0" style="width: 50px;" /> ' +
'y軸:<input type="text" readonly id="y" name="y" value="0" style="width: 50px;" /> ' +
'長度:<input type="text" readonly id="w" name="w" value="0" style="width: 50px;" /> ' +
'寬度:<input type="text" readonly id="h" name="h" value="0" style="width: 50px;" /> '
});
// 裁剪外掛
$("#prephoto").Jcrop({
minSize: [ 80, 80 ], // 最小尺寸
aspectRatio: 1, // 等寬高
onSelect: updateCoords
});
}
// 及時更新裁剪區域座標和尺寸
function updateCoords(c)
{
$('#x').val(c.x);
$('#y').val(c.y);
$('#w').val(c.w);
$('#h').val(c.h);
};
// 裁剪圖片並顯示在畫布
function cutAndShow(fileName) {
// 刪除未裁剪的圖片
$.ajax({
type: "POST",
url: "${pageContext.request.contextPath }/business/product/deleteImageAjax",
async: false,
data: {"fileName": fileName},
success: function(result) {
}
});
var x = $("#x").val();
var y = $("#y").val();
var w = $("#w").val();
var h = $("#h").val();
if (x == 0 || y == 0 || w == 0 || h == 0) {
alert("請裁剪影象");
}
var img = document.getElementById("prephoto");
var c=document.getElementById("oldConvas");
var ctx = c.getContext("2d");
ctx.drawImage(img, x, y, w, h,0,0,100,100); // 畫出圖片,尺寸100x100
var newImg = c.toDataURL("image/jpeg"); // 獲得圖片流
$(".photoStream").val(newImg); // 將圖片字元流儲存在input中
layer.closeAll(); // 關閉所有視窗
}
<!-- 這是另一個表單,用來上傳圖片和其他內容 -->
<form class="form-horizontal" role="form" method="post" onsubmit="return validate();" action="${pageContext.request.contextPath }/business/product/add.action">
<!-- 裁剪後的圖片 -->
<div class="form-group namediv">
<label class="col-sm-2" for="name" style="font-size: 15px;"></label>
<div class="col-sm-6">
<span class="text text-danger">上傳圖片要求:格式jpg/jpeg/png/gif/bmp,大小<=2m,尺寸<=500x500,裁剪後圖片尺寸為100x100</span>
<div style="width: 100px; height: 100px; border: 1px solid black;"><canvas id="oldConvas" width="100px" height="100px"></canvas></div>
<input type="hidden" class="photoStream" id="photoStream" name="photoStream" />
</div>
</div>
<!-- 其他表單內容,不寫了 -->
</form>
/**
* 非同步上傳圖片並返回圖片名稱
* @param model
* @param request
* @param response
* @param file 上傳的圖片
* @throws Exception
*/
@RequestMapping(value="getFileLocationAjax",method=RequestMethod.POST)
public void getFileLocationAjax(Model model, HttpServletRequest request, HttpServletResponse response, @RequestParam("photo") CommonsMultipartFile file) throws Exception {
if (!file.isEmpty()) {
InputStream in = file.getInputStream();
String fileName = saveImage(request, in); // 呼叫儲存圖片方法
// 儲存圖片名,方便剪下後刪除
model.addAttribute("fileName", fileName);
response.setContentType("html/text; charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.print(fileName);
}
}
/**
* 儲存圖片到上傳路徑的方法
* @param request
* @param in 圖片輸入流
* @throws Exception
* @return 返回檔名
*/
public String saveImage(HttpServletRequest request, InputStream in) throws Exception {
String fileName = UUIDUtils.getUUID() + ".jpg"; // 隨機檔名
String savePath = request.getSession().getServletContext().getRealPath("/WEB-INF/uploads") + "/"; // 獲得儲存路徑
File f = new File(savePath);
if (!f.exists()) {
f.mkdirs();
}
FileOutputStream fos = new FileOutputStream(savePath + fileName);
int b = 0;
while ((b = in.read()) != -1) {
fos.write(b);
}
fos.close();
in.close();
return fileName;
}
/**
* 非同步刪除上傳的檔案
* @param response
* @param request
* @param fileName 檔名
* @throws Exception
*/
@RequestMapping(value="/deleteImageAjax")
public void deleteImageAjax(HttpServletRequest request, String fileName) throws Exception {
System.out.println("圖片名為:" + fileName);
String savePath = request.getSession().getServletContext().getRealPath("/WEB-INF/uploads") + "/"; // 獲得儲存路徑
File file = new File(savePath + fileName);
boolean flag = false;
if (!file.exists()) {
System.out.println("檔案不存在,無法刪除");
} else {
if (file.isFile()) {
flag = file.delete();
}
}
}
/**
* 根據圖片流儲存圖片
* @param request
* @param data 圖片流
* @throws Exception
* @return 圖片名稱
*/
public String saveImageByStrem(HttpServletRequest request, String data) throws Exception {
Base64 base64 = new Base64();
// 獲得圖片流,實際圖片資料從data:image/jpeg;base64開始
byte[] b = base64.decodeBase64(data.substring("data:image/jpeg;base64,".length()));
InputStream in = new ByteArrayInputStream(b);
String fileName = saveImage(request, in); // 儲存圖片
return fileName;
}
心得
前端外掛滿天飛,經常造成選擇困難症。遇到一個外掛,覺得難用就放棄了,換一個外掛,還是嫌麻煩,又換一個,結果進入了一個死迴圈。既浪費時間,又影響心情。外掛是來簡便開發的,不是來折騰的!