如何使用File APIs來讀取文件

兼容性檢查

if (window.File && window.FileReader && window.FileList && window.Blob) {
	//支持File APIs
} else {
	//不支持File APIs
}
複製代碼

FileReader()

FileReader對象讓web應用程序能夠異步地讀取存儲在用戶電腦上的文件(或者原始數據緩衝區)的內容。在JavaScript中,FileReaderd對象經過傳入兩種相應的對象(File和Blob)來進行數據的讀取,並且這個方法在Web Workers中也能使用。web

FileReader 包括四個異步讀取文件的選項:數組

  • FileReader.readAsBinaryString(Blob|File) - 返回值的result 屬性將包含二進制字符串形式的file/blob 數據。每一個字節均由一個 [0..255] 範圍內的整數表示。
  • FileReader.readAsText(Blob|File, opt_encoding) - 返回值的result 屬性將包含文本字符串形式的 file/blob 數據。該字符串在默認狀況下采用「UTF-8」編碼。使用可選編碼參數可指定其餘格式。
  • FileReader.readAsDataURL(Blob|File) - 返回值的result 屬性將包含編碼爲數據網址的 file/blob 數據。
  • FileReader.readAsArrayBuffer(Blob|File) - 返回值的result 屬性將包含 ArrayBuffer 對象形式的 file/blob 數據。

FileReader 對象調用其中某一種讀取方法後,可以使用 onloadstart、onprogress、onload、onabort、onerror 和 onloadend 跟蹤其進度。瀏覽器

讀取文件並顯示進度

下面的示例從用戶選擇的內容中過濾掉了圖片,對文件調用 reader.readAsDataURL(),並經過將「src」屬性設爲數據網址來呈現縮略圖。bash

<style>
    .thumb {
        height: 75px;
        border: 1px solid #000;
        margin: 10px 5px 0 0;
    }

    #list {
        border: 1px solid lightgrey;
        padding: 15px;
        text-align: center;
    }

    #progress_bar {
        margin: 10px 0;
        padding: 3px;
        border: 1px solid #000;
        font-size: 14px;
        clear: both;
        opacity: 0;
        -moz-transition: opacity 1s linear;
        -o-transition: opacity 1s linear;
        -webkit-transition: opacity 1s linear;
    }

    #progress_bar.loading {
        opacity: 1.0;
    }

    #progress_bar .percent {
        background-color: #99ccff;
        height: auto;
        width: 0;
    }
</style>


<body>
    <input type="file" name="files[]" id="files" multiple />
    <div id="list"></div>
    <button onclick="abortRead();">Cancel read</button>
    <script>
    let reader;
    let progress;
    let progress_bar;

    function abortRead() {
        reader.abort();
    }

    function errorHandler(evt) {
        let error = evt.target.error;

        switch (error.code) {
            case error.NOT_FOUND_ERR:
                alert('沒有找到文件');
                break;
            case error.NOT_READABLE_ERR:
                alert('沒法讀取文件');
                break;
            case error.ABORT_ERR:
                break;
            default:
                alert('文件讀取錯誤');
        }
    }

    function updateProgress(evt) {
        if (evt.lengthComputable) {
            let percentLoaded = Math.round((evt.loaded / evt.total) * 100);
            
            if (percentLoaded < 100) {
                progress.style.width = percentLoaded + '%';
                progress.textContent = percentLoaded + '%';
            }
        }
    }

    function handleFileSelect(evt) {
        let files = evt.target.files;
        
        //建立進度條
        progress_bar = document.createElement('div');
        progress_bar.id = 'progress_bar';
        progress = document.createElement('div');
        progress.className = 'percent';
        progress.style.width = '0%';
        progress.textContent = '0%';
        progress_bar.appendChild(progress);
        document.getElementById('list').appendChild(progress_bar);

        for (let i = 0; i < files.length; i++) {
            reader = new FileReader();
            if (!files[i].type.match('image.*')) {
                alert('選擇的文件不是圖片');
                abortRead();
                return;
            }
            reader.onerror = errorHandler;
            reader.onprogress = updateProgress;

            reader.onabort = (e) => {
                alert('文件讀取已取消');
            };

            reader.onloadstart = (e) => {
            	progress_bar.className = 'loading';
            };

            reader.onload = (e) => {
            	
            	let span = document.createElement('span');
                span.innerHTML = ['<img class="thumb" src="', e.target.result, '" title="', files[i].name, '"/>'].join('');
                document.getElementById('list').insertBefore(span, progress_bar);
                progress.style.width = '100%';
                progress.textContent = '100%';
            };
            reader.readAsDataURL(files[i]);
        }
    }

    document.getElementById('files').addEventListener('change', handleFileSelect, false);
    </script>
</body>
複製代碼

See the Pen FileReader Demo by Lu (@smallbone) on CodePen.服務器

FileList API

字面上能夠理解爲多個File對象組合成的數組,可是隻有length屬性item(index)方法,訪問其中的File對象既可使用files.item(index),也可使用files[index]的方法。app

File API

File對象是一種特定類型的Blob。FileReader, URL.createObjectURL(), createImageBitmap(), 以及XMLHttpRequest.send() 都接受Blobs和Files。異步

  • File對象包含的信息
{
	lastModified: 1428005315000,
	lastModifiedDate: Thu Apr 02 2015 15:08:35 GMT-0500 (CDT),
	name: "profile.pdf",
	size: 135568,
	type: "application/pdf",
	webkitRelativePath: ""
}
複製代碼

須要注意的是,type是根據文件擴展名來判斷的,因此並非很可靠。根據上面File對象的信息其實就能夠實現一些經常使用的功能了,好比限制文件上傳的大小,初步的限制文件上傳的類型(固然也能夠經過input元素的accept屬性來實現,可是最終的類型驗證仍是須要在服務器端實現)。ui

File對象通常經過如下途徑返回的FileList對象獲取:編碼

  1. <input type="file">的元素
  2. 拖拽操做的DataTransfer對象
  3. 經過在一個HTMLCanvasElement上調用mozGetAsFile() API

經過input來選擇文件

/* 假設input元素爲<input type="file" id="upload" multiple> */
//multiple表示一次支持多個文件上傳
let uploadInput = document.getElementById('upload');
uploadInput.addEventListener('change', ()=>{
	let fileList = uploadInput.files;
	console.log(fileList);
});
複製代碼

因爲FileList對象並無forEach()方法,因此通常須要經過for循環來遍歷其中的每一個File對象:spa

for (var i = 0; fileCount = fileList.length; i < fileCount; i++) {
	console.log(fileList[i]);
}
複製代碼

可是咱們也能夠經過其餘方式來使用forEach()方法:

//1.call方法
[].forEach.call(fileList, (file, i, fileList)=>{
	...
});

//2.ES6方法
Array.from(uploadInput).forEach((i)=>{
	...
});
複製代碼

經過拖拽(drag&drop)選擇文件

拖拽事件:

  • drag(開始拖動,持續事件)
  • dragend(釋放鼠標或者按下ESC,結束拖動)
  • dragenter(進入有效的拖拽區域時)
  • dragexit(當一個元素再也不是拖動操做的直接選擇目標時)
  • dragleave(離開有效的拖拽區域時)
  • dragover(懸停在有效的拖拽區域內時,持續事件)
  • dragstart(開始拖動)
  • drop(目標放置到有效的拖拽區域時)

其中須要注意兩點:

  1. 若是dragover事件不阻止默認事件,drop事件就不會被觸發。
  2. dragexit和dragleave在不一樣瀏覽器中的觸發存在差別,dragexit在Chrome瀏覽器中就永遠不會被觸發
//拖拽和顯示區域
<div id="drop_zone">Drop files here</div>
<output id="list"></output>

<script>
	function handleFileSelect(evt) {
		evt.stopPropagation();
		evt.preventDefault();
		//注意這裏再也不是target.files
		let files = evt.dataTransfer.files;
		let output = [];
		
		[].forEach.call(files, (file)=>{
			output.push('<li><strong>', file.name, '</strong> (', file.type || 'n/a', ') - ', (file.size/1024).toFixed(3), ' Kb, last modified date: ', file.lastModifiedDate.toLocaleDateString(), '</li>');
		});
		document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';	
	}

	function handleDragOver(evt) {
		evt.stopPropagation();
		evt.preventDefault();
		evt.dataTransfer.dropEffect = 'copy';
	}

	let dropZone = document.getElementById('drop_zone');
	dropZone.addEventListener('dragover', handleDragOver, false);
	dropZone.addEventListener('drop', handleFileSelect, false);
</script>
複製代碼
相關文章
相關標籤/搜索