H 5ドラッグ&ドロップ非同期ファイルアップロードの2——アップロード進捗監視


前の《H 5ドラッグ&ドロップ+FormDataインタフェース+NodeJS,完全非同期ファイルアップロード(一)》では、ドラッグ&ドロップファイルのアップロードの流れ全体が通じましたが、実際の使用シーンからはまだ差があります.ここでは、実際にシーンを使用するために必要な機能をいくつか追加し、実際に使用するためにさらに一歩進みます.
機能の追加
  • アップロードするファイルのリストを表示します.
  • はアップロードするファイルの削除をサポートします.
  • はuploadを用いる.onprogressはアップロードの進捗状況を表示します.
  • はアップロードの中断をサポートする.

  • upload.progress
    XMLHttpRequest.uploadメソッドは、アップロードの進行状況を表すXMLhttpRequestUploadオブジェクトを返します.このオブジェクトは不透明ですが、XMLH T p RequestEventTargetとして、イベントをバインドすることで進捗状況を追跡できます.onprogresは、データ転送の傍受中です(このイベントを傍受することで、アップロードの進捗状況が得られます).
    MDNより抜粋
    実現構想.
      ファイルをアップロードエリアにドラッグ&ドロップすると、ファイルを1つのファイル配列に保存し、ファイル関連情報と削除ボタンを追加して表示し、削除ボタンをクリックしてファイル配列の対応するファイル要素を削除し、アップロードボタンをクリックしてファイル配列を巡り、アップロード待ちファイルのアップロードを開始し、削除ボタンをクリックするとファイルアップロードを中止する.
    コード#コード#
    //    
    .drop-area{
        margin:auto;
        width: 500px;
        height: 500px;
        border:1px pink dashed;
    }
    .close-btn{
        cursor: pointer;
    }
    .close-btn:after{
        float: right;
        content: 'X';
        color: red;
    }
    #fileList{
        width: 95%;
    }
    .process-bar{
        position: relative;
        margin: 0 10px 0 10px;
        width: 198px;
        height: 18px;
        display: none;
        text-align: center;
        line-height: 20px;
        color: #6dbfff;
        border-radius: 3px;
        border: 1px solid #1483d8;    
        background: #fff;
    }
    .success .process-bar,
    .uploading .process-bar{
        display: inline-block;
    }
    .process-bar .process-text{
        position: relative;
        z-index: 1;
    }
    .process-bar .process-rate{
           position: absolute;
        width: 0;
        height: 100%;
           left: 0;
          top: 0;
          border-radius: 3px;
        background: #1483d8;
    }
    .file-list .success .process-text,
    .file-list .success .close-btn:after,
    .file-list .error .process-text,
    .file-list .error .close-btn:after{
        display: none;
    }
     .success .process-bar :after,
    .error .process-bar :after{
        content:'success';
        position: absolute;    
        margin: auto;
        left: 0;
        right: 0;
        z-index: 2;
    }
    .error .process-bar:after{
        content: "error";
        color: red;
    }
    
    
    //Javascript  
    let filesSet = []; //      
    let fileList = document.getElementById('fileList'); //        DOM
    
    /**
     *              frag
     *             DOM,     
     * https://developer.mozilla.org/zh-CN/docs/Web/API/Document/createDocumentFragment
     */
    let frag = document.createDocumentFragment();
    let barDom = createProccessBar(); //     DOM
    let submit = document.getElementById("submit") //      
    
    
    /*          */
    function dragover_hander (event) {
        /*       dragover drop     
                       
                          ,  html  、    
                               
        */
        event.preventDefault(); 
    }
    
    /*      */
    function drop_hander (event) {
    
        event.preventDefault(); //      
    
        var files = event.dataTransfer.files; //  dataTransfer          
    
        for(let file of files) { //        
    
           //        DOM,    file   element   ,      
           file.element = createFileDom(file, filesSet.length)
           //     DOM,    file   processBar   ,      
           file.processBar = filesSet.length?barDom.cloneNode(true):barDom;
           //             DOM 
           file.element.appendChild(file.processBar);
           //file             
           filesSet.push(file);
           //        DOM       frag
           frag.appendChild(file.element);
        }
    
        //               div
        fileList.appendChild(frag)
    }
    
    
    /**
     *         DOM
     * file      ,        
     * index            ,    
     */
    function createFileDom (file, index) {
        let p = document.createElement('p'); 
        //file.name          
        //      ,    for...in      file        
        let text = document.createTextNode(file.name); 
        let span = document.createElement("span");
        span.setAttribute('index', index); //        index  
        span.className = 'close-btn'; 
        p.appendChild(text);
        p.appendChild(span);
    
        return p; //        DOM
    }
    /**
     *      DOM,
     *    DOM    ,   clone(true)    
     * @return {[type]} [description]
     */
    function createProccessBar() {
        let bar =  document.createElement("span");
        let rate = document.createElement("span");
        let text = document.createElement("span");
        bar.className = "process-bar";
        rate.className = "process-rate";
        text.className = "process-text";
        text.innerText="0%";
        bar.appendChild(text);
        bar.appendChild(rate);
        return bar; 
    } 
    
    //      ,         
    fileList.addEventListener('click', (evt)=>{
        let index = evt.target.getAttribute('index'); //  index   
        if (index) { //  index   ,         
    
            if (filesSet[index].unloading && filesSet[index].req) { //       
                filesSet[index].req.abort(); //    
                filesSet[index].unloading = false;    //          false    
            } else { //     
                filesSet[index].element.remove(); //  DOM
                filesSet[index].element = null; //   DOM   
                filesSet[index].processBar = null;//   DOM   
                delete filesSet[index];//                
            }
    
        }
    })
    
    submit.addEventListener('click',function(){//          
    
        //    for...in  ,                
        for(let key in filesSet){
            //              ,    
            if (filesSet[key].unloading || filesSet[key].uploaded) continue; 
            filesSet[key].unloading = true; //      
    
            //         Promise,           
            initUpload(filesSet[key]).then(file => {
                //    
                file.element.className = "success"; //UI      
                file.uploaded = true; //      
            }).catch((file, err) => {
                file.element.className = "error"; //UI      
                //        ,             
                filesSet[key].unloading = false; 
            })
        }
    })
    
    /**
     *          Promise  
     * @param  {[type]} file       
     */
    function initUpload(file){
    
        return new Promise((res, rej) => {
            let formData = new FormData();//    FormData  
            let req = new XMLHttpRequest();//  XHR  
            let reta = file.processBar.querySelector('.process-rate');//     DOM
            let text = file.processBar.querySelector('.process-text');//      DOM
            let pre;//       
            //         
            req.upload.onprogress =function(data){ 
                pre = (data.loaded/data.total*100).toFixed(2);//     
                reta.style.width = pre +'%';//     
                text.innerText = pre +'%' ;//       
            }
            //      
            req.onreadystatechange = function () {
                if (req.readyState !==4 ) return ; 
                if (req.status === 200 ){
                    //  ,      
                    res(file, req)
                } else {
                    //  ,      
                    rej(file, req)
                }
            }
            formData.append('file',file); //  append       file 
            req.open('POST', '/process_post'); //     
            req.send(formData); //    
            file.req = req; //  req  ,      
            //        
            file.element.className = "uploading"                
        })
    }

    ここでコードは終了し、完全なコードはGithubを表示できます.ローカルアップロードなので、テスト時に大きなファイルを使ったり、アップロードを制限したりすることができます.
    終わりの言葉
    これらの新しいAPIにより、ファイルのドラッグ&ドロップアップが簡単になります.残念ながら、低バージョンのIEはサポートされていません.低バージョンのIEはFalshを使ってファイルのアップロードができるそうです.具体的にはどのように実現されているのか、次の編で検討してみましょう.