js 實(shí)現(xiàn)多圖片上傳,并且展示上傳進(jìn)度

最近項(xiàng)目需要做一個(gè)如下圖所示的圖片上傳功能;


image.png

找了很多插件發(fā)現(xiàn)都不是我自己想要的,索性自己實(shí)現(xiàn)一個(gè),也可以學(xué)習(xí)一下;
主要的思想就是:
1.所有的圖片包括相機(jī)圖標(biāo)的藍(lán)色窗口(以下皆描述為相機(jī))都看成一個(gè)li,點(diǎn)擊相機(jī)選擇文件上傳;
2.讀取到文件之后,循環(huán)在相機(jī)之前插入圖片的li,具體的圖片處理,見2-1至2-5
3.如果圖片個(gè)數(shù)達(dá)到要求,隱藏相機(jī);
4.刪除圖片,將對(duì)應(yīng)的li remove掉即可,注意相機(jī)的隱藏和展示;

圖片的讀取以及上傳原理:
2-1.使用輸入框的file類型,進(jìn)行圖片的獲取
2-2.使用FileReader進(jìn)行文件的讀取,并且轉(zhuǎn)換成dataURL格式
2-3.采用canvas對(duì)圖片進(jìn)行剪裁,并且轉(zhuǎn)成dataURL格式;
2-4.將dataURL數(shù)據(jù)轉(zhuǎn)成bolb格式
2-5.將bolb添加到FormData中
2-6.使用XMLHttpRequest進(jìn)行圖片上傳,可以查看進(jìn)度;

以上就是一個(gè)完整的實(shí)現(xiàn)流程,如果不需要進(jìn)行剪裁圖片,直接原圖上傳,直接省略第2-3點(diǎn);

1.使用input的多文件上傳,設(shè)置好accept

<input type="file" multiple onchange="selectImage(this)" accept="image/gif, image/jpeg, image/png" id="upload" > 

2.使用FileReader讀取文件
在這里遇到了一個(gè)問題,開始我在load中獲取值是使用的reader.result,導(dǎo)致一直只能拿到最后一條數(shù)據(jù);然后斷點(diǎn)發(fā)現(xiàn)reader.load是異步執(zhí)行的,在for循環(huán)結(jié)束之后才能順序執(zhí)行l(wèi)oad方法,所以在load中獲取url不可使用 reader.result,必須通過load函數(shù)的參數(shù)進(jìn)行獲取值

function selectImage(imgFile) {
    var allFile = imgFile.files;
    var imageArr = [];
    for(var i=0;i<allFile.length;i++){
        var file = allFile[i];
        //添加一層過濾
        var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i;
        if(!rFilter.test(file.type)) {
            alert("文件格式必須為圖片");
            return;
        }
        var reader = new FileReader();
        reader.readAsDataURL(file); //用文件加載器加載文件
        //文件加載完成
        reader.onload = function(e) {
            //計(jì)算最后一個(gè)窗口right邊距,當(dāng)時(shí)處于第4個(gè)的時(shí)候,right=0
            if((allFile.length + 1)%4 == 0){
                document.getElementById("uploadBtn").style.marginRight = "0px";
            }
            //以下就是將所有上傳的圖片回顯到頁面上,如果需要用canvas進(jìn)行剪裁再回顯以下代碼就放入到canvas中
            var li = document.createElement('li');
            li.className = "upload-li";
            li.innerHTML = '<div class="item image">'+
                                '<img class="upload-image" src="'+e.target.result+'"/>'+
                                '<img class="delete-image" src="assets/image/image-delete.png"/>'+
                            '</div>';
            document.getElementById("uploadUL").insertBefore(li, document.getElementById("uploadBtn"));
        };
    }
}

3.如果需要對(duì)圖像進(jìn)行剪裁再回顯,加載頁面的代碼應(yīng)該放入到這一步,按照你的需求去完成;
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
img 規(guī)定要使用的圖像、畫布或視頻。
sx 可選。開始剪切的 x 坐標(biāo)位置。
sy 可選。開始剪切的 y 坐標(biāo)位置。
swidth 可選。被剪切圖像的寬度。
sheight 可選。被剪切圖像的高度。
x 在畫布上放置圖像的 x 坐標(biāo)位置。
y 在畫布上放置圖像的 y 坐標(biāo)位置。
width 可選。要使用的圖像的寬度。(伸展或縮小圖像)
height 可選。要使用的圖像的高度。(伸展或縮小圖像)
按照你自己的需求去剪裁,也可以不進(jìn)行剪裁,按照實(shí)際大小去展示;不剪裁的完整寫法如下

function canvasImg(dataURL) {
    const img = new window.Image();
    img.src = dataURL;
    var canvas = document.createElement("canvas"),
        ctx = canvas.getContext("2d");
    img.onload = function() { //圖片加載完成
        //canvas 值按照你自己的實(shí)際需求去寫
        canvas.width = 131;
        canvas.height = 131;
        ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
        document.getElementById("uploadUL").insertBefore(canvas, document.getElementById("uploadBtn"));
    };
}

將剪裁的圖片轉(zhuǎn)成dataURL格式

//圖片的質(zhì)量,這里設(shè)置的是1,也可以是小數(shù)
var quality = 1; 
//獲取畫布圖片,并且要jpg格式
var data = canvas.toDataURL("image/jpeg", quality); 
data = data.split(',')[1];

4.將dataURL轉(zhuǎn)成Blob

data = window.atob(data);
var ia = new Uint8Array(data.length);
for(var i = 0; i < data.length; i++) {
    ia[i] = data.charCodeAt(i);
}
//以上均為二進(jìn)制參數(shù)處理,從而獲取一個(gè)blob對(duì)象
var resultBolb = new Blob([ia], { 
    type: "image/jpeg"
});

5.上傳圖片

function fileUpload(resultBolb) { 
    var fd = new FormData(); 
    //向form中加入圖片數(shù)據(jù),name屬性是file
    fd.append("file", resultBolb); 
    //上傳圖片
    var xhr = new XMLHttpRequest();
    //請(qǐng)求成功
    xhr.addEventListener('load', function(resUpload) {
    }, false);
    //請(qǐng)求失敗
    xhr.addEventListener('error', function() {
    }, false);
    //上傳終止
    xhr.addEventListener('abort', function() {
    }, false);
    //上傳進(jìn)度
    xhr.upload.addEventListener('progress', function() {
    }, false);
    xhr.open('POST', "http://XXXXXXXXXXXXX"); //請(qǐng)求地址
    xhr.send(fd); //發(fā)送
}

以上就是完成的js實(shí)現(xiàn)代碼,以下簡(jiǎn)單展示一下實(shí)現(xiàn)的html+css:

<div class="upload-div">
            <ul class="upload-ul" id="uploadUL">
                <!--默認(rèn)的點(diǎn)擊窗口
                    在添加了圖片之后,循環(huán)在這個(gè)前面insert圖片的li
                -->
                <li class="upload-li" id="uploadBtn">
                    <form class="img-input-form" style="opacity: 0;">
                        <input type="file" multiple onchange="selectImage(this)" accept="image/gif, image/jpeg, image/png" id="upload" > 
                    </form>
                    <div class="item">
                        <span class="photo-span"></span>
                        <span class="circle-span"></span>
                        <span class="circle-solid-span"></span>
                    </div>
                </li>
            </ul>
        </div>

以上并不是完成代碼,后面整理一下把完整代碼放出來;歡迎提意見哦:)

很多朋友私信我到底怎么實(shí)現(xiàn)我下面把完整版代碼貼出來;
以下代碼經(jīng)過了簡(jiǎn)化,和上面教程不太一樣,主要是為了配合后臺(tái)傳數(shù)據(jù),如果想按照上面教程進(jìn)行做肯定也是沒有問題的
需要導(dǎo)入jquery包+我自己寫的一個(gè)公共方法包(需要的私信我郵箱或者評(píng)論留郵箱,我不貼出來了,太長(zhǎng)了)
我們后臺(tái)提供的接口是多圖片上傳的,所以就在選擇了圖片之后點(diǎn)擊按鈕調(diào)用fileUpload,按鈕被我整理這個(gè)demo出來的時(shí)候刪掉了,你們自己加一個(gè)按鈕吧,這個(gè)也不難的;

<!DOCTYPE html>
<html >
<head>
    <meta charset="UTF-8">
    <title></title>
    <link rel="shortcut icon" href="#" />
    <meta id="viewport" name="viewport" content="width=750">
    <meta http-equiv="Content-Type" content="multipart/form-data; charset=utf-8" />
    <style>
        html,body{
            margin: 0;
            padding: 0;
        }
        ul,li{
            margin: 0;
            padding:0;
        }
        .image-div{
            padding: 30px 50px;
            border-radius: 10px;
            background-color: #fff;
            margin: 40px 30px 0;
        }
        .image-div .title{
            height: 70px;
            line-height: 70px;
            font-size: 32px;
            color: #5f5f5f;
            font-weight: 500;
        }
        .upload-div,
        .show-div{
            /*margin-top: 20px;*/
        }
        .upload-div .upload-ul,
        .show-div .show-image-ul{
            letter-spacing: -0.5em;
        }
        .upload-div .upload-ul .upload-li,
        .show-div .show-image-ul .show-image-li{
            height: 131px;
            width: 131px;
            margin-right: 22px;
            letter-spacing: normal;
            display: inline-block;
            margin-top: 20px;
        }
        .show-div .show-image-ul .show-image-li img{
            height: 100%;
            width: 100%;
        }
        .upload-div .upload-ul .upload-li:nth-child(4n),
        .show-div .show-image-ul .show-image-li:nth-child(4n){
            margin-right: 0;
        }
        .upload-div .upload-ul .upload-li .item{
            height: 100%;
            width: 100%;
            border-radius: 10px;
            border: 3px dashed #97def1;
            position: relative;
        }
        .upload-div .upload-ul .upload-li .item.image{
            border: none;
            font-size: 0;
        }
        .upload-div .upload-ul .upload-li .item .delete-image{
            position: absolute;
            height: 25px;
            top: -12.5px;
            left: -12.5px;
        }
        .upload-div .upload-ul .upload-li .item .upload-image{
            height: 100%;
            width: 100%;
            border-radius: 10px;
            vertical-align:initial;
        }
        .img-input-form{
            position: absolute;
            height: 131px;
            width: 131px;
            z-index: 999;
        }
        .img-input-form input{
            position: absolute;
            top: 0;
            left: 0;
            height: 131px;
            width: 131px;
        }
        .photo-span{
            display: inline-block;
            position: absolute;
            height: 32px;
            width: 39px;
            border-radius: 5px;
            border: 3px solid #97def1;
            left: 50%;
            top: 50%;
            margin-top: -16px;
            margin-left: -19.5px;
        }
        .circle-span{
            display: inline-block;
            position: absolute;
            height: 14px;
            width: 14px;
            border-radius: 7px;
            border: 3px solid #97def1;
            left: 50%;
            top: 50%;
            margin-top: -7px;
            margin-left: -7px;
        }
        .circle-solid-span{
            display: inline-block;
            position: absolute;
            height: 4px;
            width: 4px;
            border-radius: 2px;
            background-color: #97def1;
            left: 50%;
            top: 50%;
            margin-top: -10px;
            margin-left: 9px;
        }
        .btm-btn{
            height: 100px;
            width: 500px;
            border-radius: 50px;
            text-align: center;
            font-size: 26px;
            color: #fff;
            background-color: #ff7e00;
            margin-left: 125px;
            margin-top: 40px;
            margin-bottom: 40px;
        }
        
    </style>
</head>

<body>
<div class="content" id="content">
    <div class="image-div" id="uploadDiv">
        <div class="title" id="imageTitle">
            上傳圖片(最多4張)
        </div>
        <div class="upload-div" id="uploadImageDiv">
            <ul class="upload-ul" id="uploadUL">
                <!--默認(rèn)的點(diǎn)擊窗口
                    在添加了圖片之后,循環(huán)在這個(gè)前面insert圖片的li
                -->
                <li class="upload-li" id="uploadBtn">
                    <form class="img-input-form" enctype="multipart/form-data"  style="opacity: 0;">
                        <input type="file" multiple onchange="selectImage(this)" accept="image/gif, image/jpeg, image/png" id="upload" >
                    </form>
                    <div class="item">
                        <span class="photo-span"></span>
                        <span class="circle-span"></span>
                        <span class="circle-solid-span"></span>
                    </div>
                </li>
            </ul>
        </div>
    </div>
</div>
<script src="assets/js/libs/jquery-2.1.1.min.js"></script>
<script src="assets/js/libs/yutil-1.0.1.js"></script>
<script type="text/javascript" charset="utf-8">
    var uploadImgIndex = 0;
    var imgArr = [];
    
    function selectImage(imgFile){
        var allFile = imgFile.files;
        var totalLen = allFile.length;
        if(yValidate.checkNotEmpty(imgArr) && imgArr.length>0){
            totalLen = totalLen + imgArr.length;
        }
        if(totalLen>4){
            alert("只能上傳4張圖片");
            return;
        }
        for(var i=0;i<allFile.length;i++){
            var file = allFile[i];
            if(yValidate.checkNotEmpty(imgArr) && imgArr.length>0){
                if(imgArr.length <4){
                    imgArr.push(file);
                }
            }else{
                imgArr.push(file);
            }
            //添加一層過濾
            var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i;
            if(!rFilter.test(file.type)) {
                alert("文件格式必須為圖片");
                return;
            }
            var reader = new FileReader();
            reader.readAsDataURL(file); //用文件加載器加載文件
            //文件加載完成
            reader.onload = function(e) {
                //計(jì)算最后一個(gè)窗口right邊距,當(dāng)時(shí)處于第4個(gè)的時(shí)候,right=0
                if((allFile.length + 1)%4 == 0){
                    document.getElementById("uploadBtn").style.marginRight = "0px";
                }
                //以下就是將所有上傳的圖片回顯到頁面上,如果需要用canvas進(jìn)行剪裁再回顯以下代碼就放入到canvas中
                var li = document.createElement('li');
                li.id = "upload_"+uploadImgIndex;
                document.getElementById("uploadBtn").style.display = "";
                uploadImgIndex++;
                li.className = "upload-li";
                li.innerHTML = '<div class="item image">'+
                    '<img class="upload-image" src="'+e.target.result+'"/>'+
                    '<img class="delete-image" src="assets/image/image-delete.png"/>'+
                    '</div>';
                document.getElementById("uploadUL").insertBefore(li, document.getElementById("uploadBtn"));
                var uploadArr = document.getElementById("uploadUL").querySelectorAll("li");
                var len = uploadArr.length ;
                if(len > 4){
                    document.getElementById("uploadBtn").style.display = "none";
                }
            };
            reader.onloadend = function(e) {
                $(".delete-image").off('click');
                $(".delete-image").on('click',function(){
                    // alert("dasd");
                    var li = $(this).parent().parent()[0];
                    var index = $(".upload-ul .upload-li").index(li);
                    var liId = li.id;
                    $("#"+liId).remove();
                    imgArr.splice(index,1);
                    document.getElementById("uploadBtn").style.display = "";
                });
            }
        }
    }
    
    function fileUpload(){
        var param = new FormData();
        for(var i=0; i<imgArr.length;i++){
            param.append('file[]', imgArr[i], i);
        }
        param.append("orderId", req.id);
        param.append("userId", bxUserData.id);
        $("body").mLoading("show");
        $.ajax({
            url:url,
            type:'POST',
            data:param,
            async: false,
            cache: false,
            contentType: false,
            processData: false,
            success:function(data){
                //請(qǐng)求成功
                
            },
            error:function(){
                //請(qǐng)求失敗
                alert(res.description || res.message || "上傳失敗");
            }
        });
    }
</script>
</body>
</html>

---------------------------------------190102更新------------------------------------
代碼放在了git,地址:https://github.com/super-jingjing/multipleUploadImage.git
不用給我留郵箱了哈,一直發(fā)郵件感覺有點(diǎn)麻煩

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容