1. 程式人生 > >Jcrop外掛+Canvas實現圖片上傳預覽+圖片裁剪上傳

Jcrop外掛+Canvas實現圖片上傳預覽+圖片裁剪上傳

前言

想實現一個功能:使用者點選上傳按鈕,選擇圖片後。圖片顯示在一個彈出框上,並可以對圖片進行裁剪。裁剪後的圖片顯示在頁面上。提交表單即可上傳圖片。

遇到問題

  1. 瀏覽器的安全設定不讓使用者獲取上傳的圖片路徑,實際獲取的是c:\fakepath\a.jpg(假設圖片是c盤下的a.jpg)
  2. ajax如何非同步上傳圖片?
  3. Canvas畫裁剪的圖片時長寬不一樣(本來想畫100x100的圖片,結果在畫布上顯示100x50)
  4. Canvas上的圖片怎麼上傳?

問題解決

  1. 選擇圖片後,觸發input的onchange事件:傳送非同步請求將圖片上傳,返回圖片的名稱,然後彈框,將圖片顯示出來。
  2. 將表單封裝為FormData型別,ajax進行一些設定即可,具體百度有說。
  3. 設定Canvas寬高我用的是style:width:100px;height:100px;,後來直接寫Canvas的屬性width=100px height=100px,問題就解決了。具體原因百度有說。
  4. 獲取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;">&nbsp;&nbsp;</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,大小&lt=2m,尺寸&lt=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;
    }

心得

前端外掛滿天飛,經常造成選擇困難症。遇到一個外掛,覺得難用就放棄了,換一個外掛,還是嫌麻煩,又換一個,結果進入了一個死迴圈。既浪費時間,又影響心情。外掛是來簡便開發的,不是來折騰的!