今天我也來標題黨一會,用“面試題”蹭一蹭熱度,主要還行想深度剖析一下,檔案上傳,裡面的門道。
1、在web上怎麼實現檔案上傳
在我們使用的各種類庫,框架中檔案上傳長相多樣,百花齊放,但是歸根結底還是離不開一個input標籤,據我所知,所有的檔案上傳都是 html(如有別的方式請大佬指正) input標籤 來實現上傳,寫法如下:
<input type="file" id="file" />
複製程式碼
長相如下:
我們發現最原始的的上傳,其實就就張這樣,樸實無華,後來的那些框架和庫,其實就是隱藏了這個樸實無華的input 而加入了很多華麗的樣式,來操縱這個input 比如你看餓了麼的這個上傳改的相當的花裡胡哨,那我們上傳之後會得到什麼呢?如下圖: 其實這個input的dom物件下面會有個fileList物件,上傳之後會儲存當前這個檔案的所有資訊,實際上,這個fileLlist,就是一個blob物件,什麼是blob物件
Blob,Binary Large Object的縮寫,代表二進位制型別的大物件。Blob的概念在一些資料庫中有使用到,例如,MYSQL中的BLOB型別就表示二進位制資料的容器。在Web中,Blob型別的物件表示不可變的類似檔案物件的原始資料,通俗點說,就是Blob物件是二進位制資料的容器,用直觀的方式去描述這個二進位制資料 實際上這個fileList就是一個特殊的blob物件
blob如何使用呢?
構建一個Blob物件通常有三種方式:
1、通過Blob物件的建構函式來構建。
2、從已有的Blob物件呼叫slice介面切出一個新的Blob物件。
3、canvas API toBlob方法,把當前繪製資訊轉為一個Blob物件。
下面只看第一種的實現
//建構函式來構建
var blob = new Blob(array[optional], options[optional]);
複製程式碼
建構函式,接受兩個引數
第一個引數:為一個資料序列,可以是任意格式的值,例如,任意數量的字串,Blobs 以及 ArrayBuffers。 第二個引數:用於指定將要放入Blob中的資料的型別(MIME)(後端可以通過列舉MimeType,獲取對應型別
Blob物件的基本屬性:
size :Blob物件包含的位元組數。(只讀)
type : Blob物件包含的資料型別MIME,如果型別未知則返回空字串。
Blob物件的基本方法:
大檔案分割 (slice() 方法),slice方法與陣列的slice類似。
此時一個blob物件就建立好了,在上一部分中,我說fileList是個特殊的blob,你可以發現他其實是在blob的兩大屬性上加了幾個別的屬性,來具體的描述整個檔案
blob有啥作用呢?
1、大檔案上傳
得益於blob的slice方法
當要上傳大檔案的時候,此方法非常有用,可以將大檔案分割分段,然後各自上傳,因為分割之後的 Blob 物件和原始的是獨立存在的。
不過目前瀏覽器實現此方法還沒有統一,火狐使用的是 mozSlice() ,Chrome 使用的是 webkitSlice() ,其他瀏覽器則正常的方式 slice()
//這裡提供一個相容寫法
function sliceBlob(blob, start, end, type) {
type = type || blob.type;
if (blob.mozSlice) {
return blob.mozSlice(start, end, type);
} else if (blob.webkitSlice) {
return blob.webkitSlice(start, end type);
} else {
throw new Error("This doesn't work!");
}
複製程式碼
生成Blob連結,用於隱藏真實連結
某個時間開始我們開啟除錯工具去看各大視訊網站的視訊src會發現,它們統統變成了這樣的形式。
這其實是為了防止盜鏈,而讓後臺傳入的一段二進位制流,我們在給包裝成blob物件,存在記憶體中後,在給轉成可以播放的連結,這樣就有效防止了真是連結的洩露,接下來我們一步步深度剖析(可能有不對之處,請大佬隨之批評指正)! 1、首先第一步,我們得有一個視訊網址,然後,我們通過ajax獲取 2、第二部後臺得給這個連結轉化成一個二進位制的流,我們用blob物件,給他裝進去, 3、用URL.createObjectURL方法,生成一個blob url 4、給這個blob url賦值到video的src上,瀏覽器就會自動解析地址,播放視訊廢話少說,下上程式碼
//建立XMLHttpRequest物件
var xhr = new XMLHttpRequest();
//配置請求方式、請求地址以及是否同步
xhr.open('POST', '二進位制流的地址', true);
//設定請求結果型別為blob
xhr.responseType = 'blob';
//請求成功回撥函式
xhr.onload = function(e) {
if (this.status == 200) {//請求成功
//獲取blob物件
var blob = this.response;
//獲取blob物件地址,並把值賦給容器
document.getElementById("video").src = URL.createObjectURL(blob);
}
};
複製程式碼
上述程式碼有一個知識點:
URL.createObjectURL
URL.createObjectURL() 靜態方法會建立一個 DOMString,其中包含一個表示引數中給出的物件的URL。這個 URL 的生命週期和建立它的視窗中的 document 繫結。這個新的URL 物件表示指定的 File 物件或 Blob 物件。
也就是說使用這個方法去建立一個DOMstring 引用這這個記憶體中的二進位制流,然後在賦值到video標籤上去就能達到隱藏連結的目的
var debug = { hello: "world" };
var blob = new Blob([JSON.stringify(debug, null, 2)], { type: 'application/json' });
console.log(blob)
var url = URL.createObjectURL(blob)
console.log(url)
複製程式碼
如此真實的連結就會被隱藏,並且這個連結是會動態變化,他在被video解析之後指向的地址就是個二進位制檔案的空間地址,看不見摸不著
解析到此,迴歸正題
2、現在上傳圖片的時候提前預覽到圖片怎麼辦?
廢話少說先上程式碼:
//html
<input type=file>
複製程式碼
//js
//拿到當前的input
const input = document.querySelector('input[type=file]')
//監聽改變,如此能拿到檔案上傳的特殊的blob物件,上文介紹過
input.addEventListener('change', () => {
//函式被執行,說明已經上傳了檔案
console.log(input.files)
//new一個fileReader物件,至於為啥先賣個關子
const reader = new FileReader()
reader.readAsDataURL(input.files[0]) // input.files[0]為第一個檔案
//成功之後賦值
reader.onload = () => {
const img = new Image()
img.src = reader.result
document.body.appendChild(img) // reader.result為獲取結果
}
}, false)
複製程式碼
效果如下,我們發現我圖片還沒呼叫介面上傳到伺服器呢,就已經能預覽了
下面我們來說一下賣的這個關子
FileReader是啥?
FileReader 物件允許Web應用程式非同步讀取儲存在使用者計算機上的檔案(或原始資料緩衝區)的內容,使用 File 或 Blob 物件指定要讀取的檔案或資料。
下面我們開看看他的一臺方法:
FileReader.readAsArrayBuffer()
開始讀取指定的 Blob中的內容, 一旦完成, result 屬性中儲存的將是被讀取檔案的 ArrayBuffer 資料物件.
**FileReader.readAsBinaryString() **
開始讀取指定的Blob中的內容。一旦完成,result屬性中將包含所讀取檔案的原始二進位制資料。
FileReader.readAsText()
開始讀取指定的Blob中的內容。一旦完成,result屬性中將包含一個字串以表示所讀取的檔案內容。
FileReader.readAsDataURL()
開始讀取指定的Blob中的內容。一旦完成,result屬性中將包含一個data: URL格式的Base64字串以表示所讀取檔案的內容。這個是我們要用到的,因為他的表示方式能被img的src讀取列印結果如下:
相信搞過前端的人都不陌生這些base64的字串,其實就是一段能表示出來的二進位制檔案,至於為啥能解析成圖片這裡就不展開講了,有興趣自行百度,一堆答案
那有人又會問了FileReader.readAsDataURL 和URL.createObjectURL有啥區別呢?
FileReader.readAsDataURL 和URL.createObjectURL區別
1、返回值
FileReader.readAsDataURL(blob)可以得到一段base64的字串
URL.createObjectURL(blob)得到的是當前檔案的一個記憶體url
複製程式碼
2、記憶體使用
FileReader.readAsDataURL(blob)得到一段超長的base64的字串,代表的是個二進位制
URL.createObjectURL(blob)得到的是一個blob開頭url地址 指向的是這個二進位制地址
複製程式碼
3、記憶體清理
FileReader.readAsDataURL(blob)依照js垃圾回收機制自動從記憶體中清理
URL.createObjectURL(blob)存在於當前document內,清除方式只有upload()事件或者revokeObjectURL手動清除
複製程式碼
4、執行方式
FileReader.readAsDataURL(blob)通過回撥的方式f返回,非同步執行;
URL.createObjectURL(blob) 直接返回,同步執行;
複製程式碼
5、多個檔案
FileReader.readAsDataURL(blob)同時處理多個檔案時,需要一個檔案對應一個FileReader物件;
URL.createObjectURL(blob) 依次返回,沒有影響;
複製程式碼
更多詳細區別參考:blog.csdn.net/qq_36671474…
總結
到這裡啊順利的實現一個提前預覽圖片的功能,核心就是利用前端的一些物件去將檔案資源存存起來,不管存入記憶體或者字串,然後讀取即可,文章屬於現學現賣,記錄學習點滴,不對之處,還請大佬指正!