2019-10-20

20521 ワード

HTML5 File API
HTML 5のinputタグに、type=fileプロパティのフォームコントロールが追加されました.このコントロールを使用すると、ファイル選択ウィンドウを呼び出してファイルの内容を読み取ることができます.


上のコードはfile DOMです.選択をサポートします.jpg、.jpge、.gif、.png接尾辞形式の画像.ファイルを選択するとinput要素がchangeイベントをトリガーします.この要素は、選択ファイルをクリックするだけでなく、ドラッグ&ドロップ選択ファイルもサポートします.ファイルをinput要素の上にドラッグ&ドロップすると、changeイベントもトリガーされます.
ファイルAPIでは、ユーザーが選択したファイルを表すオブジェクトFileを含むFilleListにアクセスできます.
const fileIpt = document.getElementById("file-ipt");
// change     :
fileIpt.onchange = function(){
    const files = fileIpt.files;    //     fileList
    for(let i = 0;i < files.length;i ++){
        console.log(files[i]);
    }
}

file inputの使い方
正直、file 要素がページに表示されている様子はあまり面白くありません.file input要素を使用するUIコンポーネントの多くは、この要素を隠し、いくつかのテクニックでファイルアップロードコンポーネントをきれいにします.
click()法によるシミュレーション

    
    


    
        const fileIpt = document.querySelector('#file-ipt');
        const btn = document.querySelector(".btn");

        btn.addEventListener('click',function(){
            if(fileIpt){
                //    button  ,    fileIpt     
                fileIpt.click();
            }
        },false);

    


上のコードが実行されるとbuttonをクリックしてもファイル選択ウィンドウが呼び出されます.
Label要素の使用
Label要素は、要素と関連付けることができる.にid属性を与える必要があります.には、inputのidと同じ値を持つfor属性が必要です.

    
    


上のコードを実行し、labelの文字をクリックすると、ファイル選択ボックスがポップアップします.labelを使用すると、clickイベントを定義またはシミュレートする必要はありません.
以下はCSSで最適化したfile inputコンポーネントです.

CSSスタイル
*{
    padding: 0;
    margin: 0;
}
.wrapper{
    width: 200px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    margin: 100px auto;
}
.label-file{
    display: flex;
    flex-direction: column;
    width: 100px;
    height: 100px;
    border: 1.5px dashed #999999;
    color: #999999;
    cursor: pointer;
    justify-content: center;
    align-items: center;
    margin-bottom: 10px;
    border-radius: 6px;
}
.label-file:hover{
    border: 1.5px dashed #1890ff;
    transition: all 0.6s;
}
span.add{
    font-size: 40px;
}
span.describe{
    font-size: 16px;
}
.wrapper p{
    padding: 6px 0px;
}

JavaScript
const fileIpt = document.getElementById("file-ipt");
const wrapper = document.querySelector(".wrapper");

fileIpt.addEventListener('change',function(){
    const file = fileIpt.files[0];
    let p = document.createElement("p"),
        name = file.name;
    p.innerText = name;
    wrapper.appendChild(p);
},false);

ドラッグ&ドロップによるファイルの選択
file input要素はデフォルトでドラッグ&ドロップをサポートします.ドラッグ&ドロップを使用してファイルを選択する場合は、file inputを使用する必要はありません.要素を作成してdropイベントを受信すればよい.
やはり上のHTML+CSS解构です.ただし、マウスドラッグイベントを追加します.
const fileIpt = document.getElementById("file-ipt");
const wrapper = document.querySelector(".wrapper");
const dropBox = document.querySelector(".label-file");

//          
function drag(e){
    e.stopPropagation();
    e.preventDefault();
}

function handleFiles(files){
    for(let i = 0;i < files.length;i ++){
        var p = document.createElement("p");
        p.innerText = files[i].name;
        wrapper.appendChild(p);
    }
    //       fileIpt        
    fileIpt.files = files;
}

//      
function drop(e){
    e.stopPropagation();
    e.preventDefault();

    //      ,drop     
    //           
    var dt = e.dataTransfer;
    var files = dt.files;

    handleFiles(files);
}

dropBox.addEventListener("dragenter",drag,false);
dropBox.addEventListener("dragover",drag,false);
dropBox.addEventListener("drop", drop, false);

画像のプレビューを表示URL.createObjectURL()の方法を使用して実施することができる.パラメータは、URLを作成するためのFileオブジェクト、Blobオブジェクト、またはMediaSourceオブジェクトです.上のコードに加えて、次のコードを追加します.
fileIpt.onchange = function(){
    const files = this.files;
    var ul = document.createElement("ul");
    for(let i = 0;i < files.length;i ++){
        var li = document.createElement("li");
        li.classList.add("imgWrapper");
        ul.appendChild(li);
        //        
        var image = new Image(200);
        //      url         url
        image.src = window.URL.createObjectURL(files[i]);

        image.onload = function() {
            window.URL.revokeObjectURL(this.src);
        }

        li.appendChild(image);
    }
    wrapper.appendChild(ul);
}

なお、これらのURLオブジェクトが不要になった場合、各オブジェクトはURL.revokeObjectURL(src)メソッドを呼び出すことによって解放される必要がある.ブラウザはドキュメントが終了すると自動的に解放されますが、最適なパフォーマンスとメモリの使用状況を得るためには、安全なタイミングで自動的に解放する必要があります.
FileReaderを使用したプレビューの作成
これは、上記のfileIpt.onchangeイベントを書き換える必要があります.
fileIpt.onchange = function(){
    const files = this.files;
    var ul = document.createElement("ul");
    for(let i = 0;i < files.length;i ++){
        var li = document.createElement("li");
        li.classList.add("imgWrapper");
        ul.appendChild(li);
        var image = new Image(200);

        li.appendChild(image);

        //             
        var reader = new FileReader();
        reader.onload = (function(aimg){
            return function(e){
                aimg.src = e.target.result;
            }
        })(image);

        //             url
        reader.readAsDataURL(files[i]);
    }
    wrapper.appendChild(ul);
}
reader.readAsDataURL(files[i])メソッドは、指定されたBlobの内容を読み取ることができる.完了すると、result(e.target.result)プロパティには、読み込まれたファイルの内容を表すdata:URL形式のBase 64文字列が含まれます.
ファイルのアップロードFormDataオブジェクトまたはFileReaderを使用してファイルアップロードを実装するか、HTML 5によって提供されるFormDataを使用して実装することができる.この3つの方法を紹介します.
FileReaderを使用してファイルをアップロードするFileReader APIは、画像プレビュー図を示す部分および使用されたことがある.アップロードファイルについては、FileReader APIの1つの方法を使用して、readAsBinaryString(blob)またはreadAsArrayBuffer(blob)のファイルアップロードの目的を達成することができる.readAsDataURL(file)メソッドはファイルにURLを生成することができ、readAsBinaryStringメソッドは指定されたBlobの内容を読み取ることができる.完了すると、resultプロパティには、読み込まれたファイルの元のバイナリデータが含まれます.一方、readAsArrayBuffer(blob)は、指定されたBlobまたはFileコンテンツを読み込むことができ、resultプロパティには、読み込まれたファイルのデータを表すArrayBufferオブジェクトが含まれます.
var reader = new FileReader();

reader.readAsArrayBuffer(blob);

reader.onload = function(e){
    //        
    var data = e.target.result;
}

次に、ファイルアップロードの例を示します.
HTMLスケルトン:

    
+
Upload

JavaScriptコード:
const imgsBox = document.querySelector(".imgsBox");
const dropBox = document.querySelector(".dropBox");
const fileIpt = document.querySelector("#file-ipt");

/**
 * Ajax     
 * @param {XMLHttpRequest} xhr
 * @param {string} method
 * @param {string} url
 * @param {Object} data
 * @param {JSON} headers
 */
function ajax(xhr,method = "GET",url,data = '',headers = {}){
    return new Promise((resolve,reject) => {
        xhr.open(method, url);
        for(let p in headers){
            xhr.setRequestHeader(p,headers[p]);
        }
        xhr.onreadystatechange = function () {
            if(xhr.readyState === 4){
                if(xhr.status === 200 || xhr.status === 304){
                    resolve(xhr.response);
                }else{
                    reject("Warning!",xhr.status);
                }
            }
        }
        xhr.send(data);
    });
}

/**
 *     
 * @param {JSON} data 
 */
function uploadFile(data){
    const xhr = new XMLHttpRequest();
    //       
    xhr.upload.addEventListener("progress", function (e) {
        if (e.lengthComputable){
            console.log((e.loaded * 100 / e.total).toFixed(2) + "%");
        }
    }, false);
    //           
    xhr.upload.addEventListener("load",function(){
        console.log("    !");
    },false);
    //       
    var result = ajax(xhr, "POST", "/file.php", data);

    result.then((data) => console.log(data));
}

/**
 *      
 * @param {string} imgURL
 */
function createImage(imgURL){
    var div = document.createElement("div"),
        box = document.createElement("div");
        img = document.createElement("img"),
    div.classList.add("imgWrapper");
    box.classList.add("imgBox");

    img.src = imgURL;
    box.appendChild(img);
    div.appendChild(box);

    img.onload = function(){
        if(img.height < img.width || img.height === img.width)
            img.style.height = "100%";
        else
            img.style.width = "100%";
    }
    imgsBox.insertBefore(div,dropBox);
}

/**
 *       
 * @param {File} file
 */
function handleFiles(file){
    var name = file.name,
        reader = new FileReader(),
        showImgReader = new FileReader();

    showImgReader.onload = function(e){
        createImage(e.target.result);
    }

    reader.onload = function(e){
        uploadFile(JSON.stringify({
            name,
            file: e.target.result,
        }));
    }
    reader.readAsBinaryString(file);
    showImgReader.readAsDataURL(file);
}

/**
 * file change     
 * @param {Event} e 
 */
function fileChange(e){
    var fileList = this.files || e.target.files;
    for(let i = 0;i < fileList.length;i ++){
        handleFiles(fileList[i]);
    }
}

fileIpt.addEventListener("change",fileChange,false);

CSSスタイル:
*{
    padding: 0;
    margin: 0;
}
div.wrapper{
    height: 500px;
    width: 500px;
    border: 1px solid #dddddd;
    margin: 100px auto;
    border-radius: 10px;
}
div.imgsBox{
    margin: 60px;
    width: 360px;
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: auto;
}
div.dropBox{
    height: 100px;
    width: 100px;
    border: 1.5px dashed #999999;
    border-radius: 6px;
    cursor: pointer;
}
div.dropBox:hover{
    border: 1.5px dashed #1890ff;
    transition: border .6s;
}
#file-ipt{
    display: none;
}
.dropBox .add{
    color: #999999;
    font-size: 50px;
    width: 100%;
    text-align: center;
}
.dropBox .describe{
    color: #999999;
    width: 100%;
    text-align: center;
}
.imgWrapper{
    height: 90px;
    width: 90px;
    padding: 8px;
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
    border-radius: 10px;
    position: relative;
    background-color: white;
    border: 1px solid #cccccc;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0px 10px 10px 0px;
}
.imgWrapper .imgBox{
    height: 100%;
    width: 100%;
    overflow: hidden;
    border-radius: 6px;
}

のコードでは、FileReaderを してファイルデータを し、POST を し、JSONデータをアップロードした.データにはファイル とファイル データが まれている.
Nodeを うjsはバックエンド を う:
const fs = require("fs");
const http = require("http");

http.createServer((req,res) => {
    if(req.url === "/file.php"){
        let data = [];
        //       
        req.on('data',(chunk) => {
            data.push(chunk);
        });
        req.on('end',function(){
            //     
            var object = JSON.parse(Buffer.concat(data).toString());
            //     
            fs.writeFileSync(`./${object.name}`,object.file,{encoding: 'binary'});
            //       
            res.setHeader("Content-Type","text/plain");
            res.end('{"status": "seccess"}');
        });
    }
    //     
    if(req.url === "/"){
        res.writeHead(200,{
            "Content-Type": "text/html"
        });
        res.end(fs.readFileSync("./01.html",{encoding: "utf8"}));
    }

    if(req.url === "/01.js"){
        res.writeHead(200,{
            "Content-Type": "text/javascript",
        });
        res.end(fs.readFileSync('./01.js',{encoding: 'utf8'}));
    }
}).listen(4000,() => {
    console.log("server is running: http://localhost:4000");
});

もちろん、readAsArrayBuffer(file)メソッドを してファイルを み り、リクエストを することもできます.なお、この はArrayBufferデータを する.ArrayBufferのデータは できません.つまり、upload(JSON.stringify({name,file}))メソッドを してJSON.stringifyデータを したため、 のようにArrayBufferを してデータを することはできません.データ は うしかありません:upload(e.target.result).
Node.jsサービス がArrayBufferデータを する は、Buffer.concat(data)でファイル き みを うだけでよい.この では、 にデータを すると、ファイルの やファイル など、 ファイルの を ることができません.ファイルの を らないと、 しいファイルを することができません.もちろん、2 のリクエストを することができます.1 はファイルデータで、1 はファイル です.
シミュレーションプログレスバー
くのファイルのアップロードまたはダウンロードシーンには、ダウンロード/アップロードの があり、 は バーで されます.XMLHttpRequestインスタンスのuploadオブジェクトでは、ファイルのアップロード/ダウンロードの をリスニングするためにprogressをリスニングできます.loadメソッドは、ファイルのアップロード/ダウンロードが したことを します. のコードは、ファイルアップロード のアップロードの を します.
xhr.upload.addEventListener("progress",function(e){
    if (e.lengthComputable){
        /*
         * lengthComputable           (    )。    false
         * loaded               (    )
         * total               (    )
        */
        console.log((e.loaded * 100 / e.total).toFixed(2) + "%");
    }
},false);

なコード:
/**
 *       
 * @param {string} tagName
 * @param {string} content
 * @param {string} className
 */
function createElem(tagName,content,className){
    const tag = document.createElement(tagName);
    tag.innerHTML = content || '';
    if(className)
        tag.classList.add(className);
    return tag;
}

/**
 *              
 * @param {Element} parent
 * @param {Array } elems
 * number    0  ,append   ,   1  ,insertBefore   
 * @param {number} position
 *    insertBefore  ,         
 * @param {Element} oldElem
 */
function appendElems(parent,elems,position,oldElem){
    if(position){
        elems.forEach(item => {
            parent.insertBefore(item,oldElem);
        });
    }else{
        elems.forEach(item => {
            parent.appendChild(item);
        });
    }
}

/**
 *           
 * @param {Event} e
 * @param {Object } elems
 */
function progressAnimation(e,elems){
    const { div,describe,progressNum,loadBox,loadBar } = elems;

    if (e.lengthComputable) {
        loadBar.style.width = "0px";
        imgsBox.insertBefore(div,dropBox);
        appendElems(div, [progressNum, loadBox, describe]);
        loadBox.appendChild(loadBar);

        describe.innerText = "Upload...";
        const bar_W = parseInt(window.getComputedStyle(loadBox,null).width);

        var num = (e.loaded * 100 / e.total).toFixed(2);
        if(num >= 90){
            loadBar.style.backgroundColor = "greenyellow";
        }
        loadBar.style.width = num * (bar_W / 100) + 'px';
        progressNum.innerText = num + "%";
    }
}

function loadAnimation(loadWrapper){
    loadWrapper.style.display = "none";
    loadWrapper.previousElementSibling.style.display = "flex";
}

/**
 *     
 * @param {JSON} data
 */
function uploadFile(data){
    const xhr = new XMLHttpRequest();
    var elems = {
        div: createElem("div", '', 'loadWrapper'),
        describe: createElem("span", '    ...', 'describe'),
        progressNum: createElem("span", '0%', ".progressNum"),
        loadBox: createElem("div", '', 'loadBox'),
        loadBar: createElem("div", '', 'loadBar')
    }
    xhr.upload.addEventListener("progress",function(e){
        progressAnimation.call(this,e,elems);
    },false);
    xhr.upload.addEventListener("load",function(){
        loadAnimation.call(this,elems.div);
    },false);
    var result = ajax(xhr, "POST", "/file.php", data);

    result.then((data) => console.log(data));
}

/**
 *      
 * @param {string} imgURL
 */
function createImage(imgURL){
    var div = document.createElement("div"),
        box = document.createElement("div"),
        img = document.createElement("img");
    div.classList.add("imgWrapper");
    box.classList.add("imgBox");

    img.src = imgURL;
    box.appendChild(img);
    div.appendChild(box);

    img.onload = function(){
        if(img.height < img.width || img.height === img.width)
            img.style.height = "100%";
        else
            img.style.width = "100%";
    }
    imgsBox.insertBefore(div,dropBox);
    div.style.display = "none";
}

CSS ファイルアップロード
.loadWrapper{
    height: 100px;
    width: 100px;
    background: white;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    border: 1px solid #cccccc;
    border-radius: 6px;
}
.loadWrapper .describe{
    color: #999999;
}
.loadWrapper .progressNum{
    color: orangered;
    font-weight: bold;
}
.loadWrapper .loadBox{
    width: 80px;
    height: 10px;
    border-radius: 5px;
    border: 1px solid #cccccc;
    margin: 6px 0px;
    box-sizing: border-box;
    overflow: hidden;
}
.loadWrapper .loadBox .loadBar{
    width: 0px;
    height: 20px;
    background: green;
}

FormDataによるファイルアップロードFormDataはHTML 5のAPIです.FormDataを してフォームを する を に します.


    
name:
password:
const form = document.getElementById("form"); const submitBtn = document.getElementsByClassName("submitBtn")[0]; // from FormData const formData = new FormData(form); function ajax(xhr, method = "GET", url, data = "", headers = {}) { return new Promise((resolve, reject) => { xhr.open(method, url); xhr.onreadystatechange = function () { if (this.readyState === 4) { if (this.status === 200 || this.status === 304) { resolve(this.response); }else{ reject("Warnning!",this.status); } } } for(let p in headers){ xhr.setRequestHeader(p,headers[p]); } xhr.send(data); }); } submitBtn.addEventListener("click", async function (e) { const xhr = new XMLHttpRequest(); var res = await ajax( xhr, "POST", form.action, // FormData formData ); console.log(res); }, false);
FormDataの例は1つのMapである.中にはappenddeletehasなどの方法があります.FormDataを使用する場合、form要素にはenctype="multipart/form-data"という属性が追加されます.追加しないと、生成されたデータはkey=value形式のデータであり、file inputがある場合、ファイル内容はアップロードされません.
form要素を使用してラップしない場合は、formData.append()メソッドを使用してデータを追加できます.append()メソッドは、2つのパラメータを受信し、最初のパラメータはデータであり、1つの文字列であってもよいし、blobオブジェクトであってもよく、fileデータはblobに属する.2番目のパラメータはオプションで、データのファイル名を表し、文字列です.
const formData = new FormData();

const file = document.getElementById("file-ipt");

formData.append(file.files[0]);     //