1. 程式人生 > >微信開發中使用微信JSSDK和使用URL.createObjectURL上傳預覽圖片的不同處理對比

微信開發中使用微信JSSDK和使用URL.createObjectURL上傳預覽圖片的不同處理對比

在做微信公眾號或者企業微信開發業務應用的時候,我們常常會涉及到圖片預覽、上傳等的處理,往往業務需求不止一張圖片,因此相對來說,需要考慮的全面一些,使用者還需要對圖片進行預覽和相應的處理,在開始的時候我使用JSSDK方式,使用微信的SDK介面進行圖片的上傳、預覽操作,後來發現通過URL.createObjectURL選定本地圖片預覽、上傳也是非常方便的,本篇隨筆針對同一個多圖片的業務需求,使用JSSDK和URL.createObjectURL兩種方式進行圖片預覽、上傳、刪除等常規的處理。

1、使用JSSDK對圖片的處理

 在一個公眾號頁面-問診介面裡面,我們需要讓使用者上傳相關的圖片,包括症狀圖片、處方圖片等,每個列表可以上傳多張圖片,如下介面所示。

這裡使用了SDK進行圖片的上傳處理,參考Weui的上傳樣式,選擇本地幾張圖片,可以看到縮圖展示在圖框裡面,但是圖片還沒有上傳,我們在儲存問診資訊的時候,才啟動圖片檔案的上傳處理。

如果圖片是在編輯介面中,我們需要考慮對現有圖片進行刪除的處理,刪除前確認即可。 

 

 單擊刪除圖示的按鈕,提示使用者進行圖片刪除確認即可。

 以上就是我們幾個圖片處理的場景,我們來看看如何實現的。

我們以症狀圖片為例,它的介面HTML部分的程式碼如下所示。

<div class="weui-cells__title">症狀圖片</div>
<div class="weui-cells weui-cells_form">
    <div class="weui-cell">
        <div class="weui-cell__bd">
            <div class="weui-uploader">
                <!--編輯的時候,放置已有圖片進行預覽-->
                <ul class="weui-weui-uploader__files" style="list-style-type: none" id="imgSickPreview"></ul>
                <div class="weui-uploader__bd">
                    <!--放置選擇的圖片進行預覽-->
                    <ul class="weui-weui-uploader__files" style="list-style-type: none" id="imgSick"></ul>
                    <div class="weui-uploader__input-box">
                        <!--圖片上傳的圖示處理-->
                        <span id="uploaderSick" class="weui-uploader__input"></span>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

為了使用微信JSSDK來實現上傳、預覽圖片的功能,我們需要定義好對應的JS介面,如下程式碼所示。

    <script language="javascript">
        var appid = '@ViewBag.appid';
        var noncestr = '@ViewBag.noncestr';
        var signature = '@ViewBag.signature';
        var timestamp = '@ViewBag.timestamp';

        wx.config({
            debug: false,
            appId: appid, // 必填,公眾號的唯一標識
            timestamp: timestamp, // 必填,生成簽名的時間戳
            nonceStr: noncestr, // 必填,生成簽名的隨機串
            signature: signature, // 必填,簽名,見附錄1
            jsApiList: [
               'checkJsApi',
               'chooseImage',
               'previewImage',
               'uploadImage',
               'downloadImage',
               'getLocalImgData'
            ]
        });

......

    </script>

在上傳圖片之前,我們需要通過JSSDK的方式選擇圖片,這裡用到了chooseImage的介面,大概所需的程式碼如下所示。

        //上傳圖片集合[用微信上傳的時候,記錄微信mediaId集合]
        var images = {
            localSickId: [],//病情
            localPresId: [],//處方

            serverSickId: [],
            serverPresId: []
        };

        //圖片選擇
        $("#uploaderSick").click(function () {
            chooseImage("imgSick", "sick");
        });
        $("#uploaderPres").click(function () {
            chooseImage("imgPres", "pres");
        });

        //選擇圖片顯示
        function chooseImage(ctrlName, type) {
            //清空集合
            if (type == "sick") {
                images.localSickId = [];
            } else {
                images.localPresId = [];
            }

            wx.chooseImage({
                count: 3, // 預設9
                sizeType: ['original', 'compressed'], // 可以指定是原圖還是壓縮圖,預設二者都有
                sourceType: ['album', 'camera'], // 可以指定來源是相簿還是相機,預設二者都有
                success: function (res) {
                    var ctrl = $("#" + ctrlName);
                    ctrl.html("");//清空圖片顯示

                    //localIds = res.localIds; // 返回選定照片的本地ID列表,localId可以作為img標籤的src屬性顯示圖片
                    if (type == "sick") {
                        images.localSickId = res.localIds;
                    } else {
                        images.localPresId = res.localIds;
                    }

                    //動態增加img標識
                    $.each(res.localIds, function (index, item) {
                        ctrl.append("<img class='weui-uploader__file' src='" + item + "' />");
                    });
                }
            });
        }

選擇圖片後,就是將圖片的縮圖動態的增加在指定圖片框裡面。然後在儲存資料的時候,使用JSSDK提交圖片到微信伺服器,我們伺服器後臺再從微信伺服器獲取圖片(通過媒體id)。

這裡我們定義了兩類的圖片,方便區分,分別是症狀圖片和處方圖片,因此需要定義兩個類別的變數,分別儲存本地和伺服器返回的id集合。

我們在進行表單提交的時候,需要確認一些必填項,然後在檢查是否有檔案需要上傳,如果有則執行上傳處理後提交表單,大概的處理程式碼如下所示。

        //上傳資料
        $("#btnOK").click(function () {
            var PatientName = $("#PatientName").val();
            if (PatientName == '' || PatientName == undefined) {
                $.toast('患者姓名不能為空', "forbidden");
                return;
            }
            var ProblemDetail = $("#ProblemDetail").val();
            if (ProblemDetail == '' || ProblemDetail == undefined) {
                $.toast('詳細描述不能為空', "forbidden");
                return;
            }

            //上傳圖片
            if (images.localSickId.length > 0 || images.localPresId.length > 0) {
                uploadImage(submitCallback);//通過就提交資料
            } else {
                submitCallback();
            }
        });

這裡主要的圖片上傳處理,就是 uploadImage 函式的處理了,而submitCallback這是定義一個函式上傳表單資料的。

由於微信JSSDK上傳圖片,是一個個上傳的,我們需要把它們串聯起來,一併上傳。uploadImage 裡面定義了一個內部函式,依次進行圖片的上傳。

我們通過序號來標識兩類圖片,圖片上傳成功後,我們把圖片媒體的id(JSSDK返回的)記錄下來,統一提交給對應資料庫記錄,在後臺進行圖片檔案的提取即可。

        //上傳圖片
        function uploadImage(callback) {
            var localIds = images.localSickId.concat(images.localPresId);//合併陣列
            var i = 0, length = localIds.length;
            //$.toast(length);

            images.serverSickId = [];
            images.serverPresId = [];

            //定義一個子級函式,方便遞迴呼叫
            function upload(callback) {
                wx.uploadImage({
                    localId: localIds[i],
                    success: function (res) {
                        i++;

                        //成功後加入不同的集合
                        if (i <= images.localSickId.length) {
                            images.serverSickId.push(res.serverId);//第一部分
                        } else {
                            images.serverPresId.push(res.serverId);//第二部分
                        }

                        if (i < length) {
                            upload(callback);
                        }else if (callback != undefined) {
                            callback();//回撥函式
                        }
                    },
                    fail: function (res) {
                        alert(JSON.stringify(res));
                    }
                });
            };

            upload(callback);
        }

其中我們的定義的callback函式,是用來最後上傳完成後,執行表單的記錄儲存的,表單包含各種輸入和圖片的ID資訊,如下是詳細的表單儲存操作程式碼。

        //在上傳圖片後觸發的回撥函式
        function submitCallback() {
            var druglist = [];//構造集合物件
            for (var key in itemDict) {
                //Drug_ID,DrugName,How,Freq
                druglist.push({
                    'Drug_ID': key, "DrugName": itemDict[key], 'How': howDict[key],
                    'Freq': freqDict[key], 'Quantity': quantityDict[key]
                });
            }
            var url = "/H5/[email protected]";
            var postData = {
                PatientName: $("#PatientName").val(),
                Gender: $("#Gender").val(),
                BirthDate: $("#BirthDate").val(),
                Telephone: $("#Telephone").val(),
                ProblemDetail: $("#ProblemDetail").val(),
                Creator: $("#Creator").val(),
                ProblemItems: $("input[name='ProblemItems']:checked").val(),
                @if (ViewBag.Info != null) { 
                <text>
                ID: '@ViewBag.Info.ID',
                </text>
                }

                SickAttachGUID: $("#SickAttachGUID").val(),
                PresAttachGUID: $("#PresAttachGUID").val(),
                ServerSickId: JSON.stringify(images.serverSickId),
                ServerPresId: JSON.stringify(images.serverPresId),
                DrugList: JSON.stringify(druglist)
            };

            $.post(url, postData, function (json) {
                //轉義JSON為物件
                var data = $.parseJSON(json);
                if (data.Success) {
                    $.toast("處方已提交稽核中,稍後請到處方查詢檢視。");
                    //WeixinJSBridge.call('closeWindow');//關閉視窗
                    location.href = "/h5/Prescription";//跳轉到處方頁面
                }
                else {
                    $.toast("儲存失敗:" + data.ErrorMessage, "forbidden");
                }
            });
        };

我們注意到,我們服務端返回的ID集合,我們分別放在了兩個欄位裡面提交到後臺處理。

    ServerSickId: JSON.stringify(images.serverSickId),
    ServerPresId: JSON.stringify(images.serverPresId),

在後臺,我們首先需要提取使用者提交的基礎表單資料,如下是後臺定義的函式處理

 

 這些是常規的表單資訊,我們提交到微信伺服器的圖片資訊也需要提取出來的,這些圖片分兩類,每類都包含多個字串組成的圖片ID集合。

後臺主要就是根據這些ID,使用微信基礎介面,獲取臨時圖片的介面方式,把圖片從伺服器上下載下來儲存到本地伺服器上。

 其中UploadFile函式就是封裝瞭如何實現圖片獲取、圖片儲存的處理邏輯,主要的程式碼部分邏輯如下所示。

 這種方式很好的利用了JSSDK的圖片選擇、上傳的處理,實現了我們所需要的圖片預覽、選擇、上傳等一系列操作,也能夠滿足實際的功能需要。

不過總感覺把圖片繞了一圈再回來不太好而已。

2、使用URL.createObjectURL對圖片的處理

 前面介紹了使用微信JSSDK方式實現圖片預覽、選擇、上傳等一系列操作,在上傳檔案的時候,感覺繞了一圈再回來,一直希望能夠直接把檔案直接提交到伺服器上更好,就像我們一般的Web應用上傳附件一樣感覺更好一些,後來發現了可以通過URL.createObjectURL進行相關的處理,參考了一些案例,對前面介紹的JSSDK的圖片上傳方式進行改良,從而實現了把圖片附件通過表單的方式直接提交到自己後臺伺服器上了,下面開始介紹一下這種方式的思路和實現程式碼。

首先我們定義一個預覽圖片的列表和一個Input的檔案控制元件元素,替代前面的做法,如下所示。

<div class="weui-cells__title">症狀圖片</div>
<div class="weui-cells weui-cells_form">
    <div class="weui-cell">
        <div class="weui-cell__bd weui-cell_primary">
            <div class="weui-uploader">
                <!--預覽圖片的列表-->
                <ul class="weui-uploader__files" id="imgSick">
                </ul>
                <div class="weui-uploader__input-box">
                    <!--上傳圖片附件控制元件-->
                    <input id="uploaderSick" class="weui-uploader__input" type="file" accept="image/*" multiple="">
                </div>
            </div>
        </div>
    </div>
</div>

為了實現選擇圖片檔案的時候,預覽圖片的列表可以動態變化(動態增加 li 專案),我們需要定義對應的事件來實現這個操作。

        //存放檔案圖片的集合
        var fileSick = new Array();
        var filePres = new Array();
        function initImage() {
            var tmpl = '<li class="weui-uploader__file" style="background-image:url(#url#)"></li>',
            $gallery = $("#gallery"),
            $galleryImg = $("#galleryImg"),

            $uploaderSick = $("#uploaderSick"),
            $imgSick = $("#imgSick"),
            $uploaderPres = $("#uploaderPres"),
            $imgPres = $("#imgPres");

            //症狀圖片上傳
            $uploaderSick.on("change", function (e) {
                var src, url = window.URL || window.webkitURL || window.mozURL,
                    files = e.target.files;

                for (var i = 0, len = files.length; i < len; ++i) {
                    var file = files[i];
                    fileSick.push(file);//加入集合

                    if (url) {
                        src = url.createObjectURL(file);
                    } else {
                        src = e.target.result;
                    }
                    $imgSick.append($(tmpl.replace('#url#', src)));
                }
            });

          ..............

我們注意到了,這裡沒有使用chooseImage的JSSDK介面,而是通過 url.createObjectURL(file) 的方式獲取路徑,展示在圖片列表控制元件裡面。

對於動態增加的圖片,我們可以讓它支援單擊預覽的方式,預覽其實是把圖片放在一個預覽層裡面。

    var index; //第幾張圖片
    var category;//那個類別
    var imgid;//圖片ID
    //症狀圖片單擊處理
    $imgSick.on("click", "li", function() {
        index = $(this).index();
        category = "sick";
        imgid = $(this).attr("id");
        $galleryImg.attr("style", this.getAttribute("style"));
        $gallery.fadeIn(100);
    });

預覽層的DIV是放在主介面上的,主介面是一個放置圖片的區域,底部是一個刪除按鈕,用來我們實現圖片刪除操作的。

<!--圖片預覽層-->
<div class="weui-gallery" id="gallery">
    <span class="weui-gallery__img" id="galleryImg" style="width:auto"></span>
    <div class="weui-gallery__opr">
        <a href="javascript:" class="weui-gallery__del">
            <i class="weui-icon-delete weui-icon_gallery-delete"></i>
        </a>
    </div>
</div>

預覽層再次單擊的時候關閉,執行的JS程式碼如下所示。

$gallery.on("click", function() {
    $gallery.fadeOut(100);
});

刪除圖片的時候,我們區分是存在伺服器的圖片,還是本地臨時選擇的圖片,區別對待。如果伺服器圖片,需要提示確認刪除,如果是本地臨時圖片,直接移除即可。

//刪除圖片(根據類別和序號處理)
$(".weui-gallery__del").click(function () {                
    console.log(index + ',' + category + ',' + imgid);//記錄顯示

    //如果是在服務端的圖片,確認後移除
    if (imgid != undefined && imgid != '') {
        $.confirm("您確定要永久刪除該圖片嗎?", "永久刪除?", function () {
            var url = "/H5/[email protected]";
            var postData = {
                id: imgid.replace(/img_/, '') //控制元件id去掉字首為真正附件ID
            };

            $.post(url, postData, function (json) {
                //轉義JSON為物件
                var data = $.parseJSON(json);
                if (data.Success) {
                    $.toptip("刪除成功!", 'success');

                    //在介面上找到對應控制元件ID,移除控制元件
                    RemoveImg();
                }
                else {
                    $.toast("操作失敗:" + data.ErrorMessage, "forbidden");
                }
            });
        });
    } else {
        RemoveImg(); //普通圖片快速移除
    };
});

其中移除圖片顯示的JS程式碼如下所示。

//移除對應類別的圖片
function RemoveImg() {
    if (category == "sick") {
        $imgSick.find("li").eq(index).remove();
        fileSick.splice(index, 1);
    } else {
        $imgPres.find("li").eq(index).remove();
        filePres.splice(index, 1);
    }
};

我們要使用表單上傳檔案的方式,就需要在JS裡面建立一個FormData的物件,用來承載檔案內容,如下所示

var formData = new FormData();//構建一個FormData儲存複雜物件

如果是常規的表單資料,我們通過鍵值,把內容填入FormData即可,如下所示。

var formData = new FormData();//構建一個FormData儲存複雜物件
formData.append("PatientName", $("#PatientName").val());

如果是圖片附件的,我們則需要遍歷集合檔案,把它們逐一加入對應鍵值裡面,為了區分不同的類別檔案,我們使用不同的字首方式,如下程式碼所示。

//加入症狀圖片
for (var i = 0; i < fileSick.length; i++){
    formData.append("sick_" + i, fileSick[i]);
};
//加入處方圖片
for (var i = 0; i < filePres.length; i++){
    formData.append("pres_" + i, filePres[i]);
};
//提交表單資料和檔案            
var url = "/H5/[email protected]";
$.ajax({
    url: url,
    type: 'post',
    processData: false,
    contentType: false,
    data: formData,
    success: function (json) {
        //轉義JSON為物件
        var data = $.parseJSON(json);
        if (data.Success) {
            $.toast("處方已提交稽核中,稍後請到處方查詢檢視。");
            //WeixinJSBridge.call('closeWindow');//關閉視窗
            location.href = "/h5/Prescription";//跳轉到處方頁面
        }
        else {
            $.toast("儲存失敗:" + data.ErrorMessage, "forbidden");
        }
    }
});

 在後臺的處理函式 DrugInquirySave2 裡面,我們需要把檔案按鍵名提取出來,根據檔案鍵名的不同,放到不同給的集合裡面儲存起來即可。

如下是DrugInquirySave2 函式裡面的部分程式碼,用來處理收到的表單檔案集合。然後我們在把檔案寫入檔案系統即可,這樣省卻了對JSSDK提交檔案,再去微信伺服器提取檔案方式的麻煩,直接由客戶端把檔案上傳的自己的檔案伺服器了。

#region 通過檔案附件方式獲取
var files = Request.Files;
if (files != null && files.Count > 0)
{
    LogTextHelper.Info(string.Format("收到檔案:{0}", files.Count));//測試

    foreach (string key in files.Keys)
    {
        LogTextHelper.Info(string.Format("收到檔案key:{0}", key));

        var fileData = files[key];
        bool isSickImage = key.ToLower().IndexOf("sick") >= 0;//判斷是否為問診圖片分類
        if (fileData != null)
        {
            HttpContext.Request.ContentEncoding = Encoding.GetEncoding("UTF-8");
            HttpContext.Response.ContentEncoding = Encoding.GetEncoding("UTF-8");
            HttpContext.Response.Charset = "UTF-8";

            string fileName = Path.GetFileName(fileData.FileName);      //原始檔名稱
            string fileExtension = Path.GetExtension(fileName);         //副檔名

            FileUploadInfo fileInfo = new FileUploadInfo();
            fileInfo.FileData = ReadFileBytes(fileData);
            if (fileInfo.FileData != null)
            {
                fileInfo.FileSize = fileInfo.FileData.Length;
            }
            //判斷圖片類別分組
            fileInfo.Category = isSickImage ? "問診圖片" : "處方圖片";
            fileInfo.FileName = fileName;
            fileInfo.FileExtend = fileExtension;
            //判斷屬於那個分組【這裡只有兩個分組】
            fileInfo.AttachmentGUID = isSickImage ? SickAttachGUID : PresAttachGUID;

            fileInfo.AddTime = DateTime.Now;//建立時間
            fileInfo.Editor = openid;//記錄人
            fileInfo.Owner_ID = info.ID;//屬於主表記錄

            result = BLLFactory<FileUpload>.Instance.Upload(fileInfo);
            if (!result.Success)
            {
                LogTextHelper.Error("上傳檔案失敗:" + result.ErrorMessage);
            }
        }
    }
}
#endregion

編輯現有記錄的時候,也可以實現對已有圖片的刪除操作,臨時檔案的預覽處理和再次上傳等操作。

 

3、兩種圖片處理方式的總結

本篇隨筆是基於公眾號上傳圖片檔案的兩種方式的處理,分別是使用微信JSSDK和使用URL.createObjectURL上傳預覽圖片的不同處理對比,兩種方式都能夠滿足圖片的處理操作。對比處理程式碼,可能使用後者可能更加簡潔一些。而且微信瀏覽器對URL.createObjectURL的支援也非常不錯,可以在微信開發工具和實際環境上都正常使用。

&n