摘要:簡介本篇文章主要探討中的數據操作一直以來給人一種比較低能的感覺例如無法讀取系統上的文件不能做一些底層的操作所以在頁面上操作數據會交由服務器處理也就成了主流的做法但是很多人沒有發現實際上以及在逐步增強這些功能現在我們就已經可以放心的在端進行文
簡介
本篇文章主要探討JavaScript中的數據操作.
JavaScript一直以來給人一種比較低能的感覺,例如無法讀取系統上的文件,不能做一些底層的操作.
所以在頁面上操作數據會交由服務器處理也就成了主流的做法.
但是很多人沒有發現,實際上JavaScript以及在逐步增強這些功能,現在我們就已經可以放心的在web端進行文件操作了.
起因N個月前我去新浪面試實習,我提到了原來我做過一個頁面配合上傳Excel可以完成一些功能.
我的這番話勾起了面試官在實際編碼中遇到了一些問題,就是如何不通過服務器來操作數據,我和她討論了一番,最后不了了之了(當然也沒過).
N個月后實習被坑,成了無業游民閑來無事正好也好奇這個問題然后就研究了一下.
涉及的內容沒有非必要的內容,對于文件操作來說以下API都是必須了解的,本文也會漸進式的討論這些內容.
Blob
ArrayBuffer
TypedArray
DataView
FileReader
File
URL
兼容性我沒有詳細考證API的兼容性,不過從MDN提供的數據來看IE10以上的瀏覽器大部分都是兼容的.
總覽一般來說操作一個文件都要經歷如下的步驟:
知道文件的地址(存放的位置)
讀取
保存到Buffer中,重復上步驟直至結束
進行數據編輯
知道要寫入的地址
獲取要寫入的數據,從Buffer中獲取還是所有數據
寫入
寫入完成
API名稱以及對應的職責:
名稱 | 職責 |
---|---|
URL | 制造文件地址 |
FileReader | 讀取文件的接口 |
Blob | 用于在JavaScript表示文件 |
File | 用于表示文件對象 |
ArrayBuffer | 表示Buffer(僅僅提供一片內存空間) |
TypedArray | 基于數組操作Buffer上的數據(操作的最小單位是數組元素) |
DataView | 基于字節操作Buffer上的數據 |
上面描述的內容之間的關系很復雜,這里我們逐步來進行分析.
ArrayBufferhttps://developer.mozilla.org...
ArrayBuffer對象用于表示一段緩沖區域(可以理解為一段可控的內存區域),它僅僅表示這片被開辟的區域但是不提供操作方式.
const arraybuffer = new ArrayBuffer(8) // 創建一個長度為8字節大小的Buffer
默認ArrayBuffer中每一個字節都被填充了0.
利用這個對象我們可以完成如下的操作:
獲取
該Buffer的大小(字節)
該Buffer的副本(范圍)
修改
該Buffer的大小
判斷
給定的數據是否是操作視圖(實例方法)
異常
當創建的Buffer長度超過Number.MAX_SAFE_INTEGER的大小會產生錯誤
const arraybuffer = new ArrayBuffer(8); console.log(arraybuffer.byteLength); // 獲取長度 console.log(arraybuffer.slice(4,8)); // 獲取副本 // 截止到2019年2月12日 20:11:05沒有瀏覽器實現該功能 console.log(arraybuffer.transfer(arraybuffer,16));// 修改原有Buffer console.log(ArrayBuffer.isView({})) // false 是否是視圖DataView
https://developer.mozilla.org...
DataView用于操作ArrayBuffer中的數據,這也是它構造函數中接受一個ArrayBuffer的原因:
const arraybuffer = new ArrayBuffer(8); const dataview = new DataView(arraybuffer); // 默認的視圖大小就是buffer的大小 const offset = new DataView(arraybuffer, 0, arraybuffer.byteLength); // 默認的偏移量以及長度
利用這個對象我們可以完成如下的操作:
獲取
被該視圖引入的Buffer(只讀)
該視圖從Buffer中讀取的自己長度(只讀)
該視圖從Buffer中讀取的偏移量(只讀)
異常
如果由偏移(byteOffset)和字節長度(byteLength)計算得到的結束位置超出了 buffer 的長度.
寫入
使用xxx類型寫入(見下方)
讀取
使用xxx類型讀取
可以使用的類型:
類型名稱 | 對應的方法 |
---|---|
Int8 | getInt8,setInt8 |
Uint8 | getUint8,setUint8 |
Int16 | getInt16,setInt16 |
Uint16 | getUint16,setUint16 |
Int32 | getInt32,setInt32 |
Uint32 | getUint32,setUint32 |
Float32 | getFloat32,setFloat32 |
Float64 | getFloat64,setFloat64 |
簡單實例:
const arraybuffer = new ArrayBuffer(1); // 一個字節 const dataview = new DataView(arraybuffer); // 默認的視圖大小就是buffer的大小 dataview.setInt8(0,127) // 從0開始寫入一個int8(8位無符號整形,一個字節) dataview.getInt8(0) // 從偏移0開始讀取一個int8(8位無符號整形,一個字節) console.log(dataview.getInt8(0)); dataview.setInt16(0,65535); // 錯誤超出了ArrayBuffer的空間 int16占兩個字節字節序
簡單來講-使用DataView:
在讀寫時不用考慮平臺字節序問題。
https://developer.mozilla.org...https://zh.wikipedia.org/wiki...
可以利用這個函數來進行判斷:
var littleEndian = (function() { var buffer = new ArrayBuffer(2); new DataView(buffer).setInt16(0, 256, true /* 設置值時使用小端字節序 */); // Int16Array 使用系統字節序,由此可以判斷系統是否是小端字節序 return new Int16Array(buffer)[0] === 256; })(); console.log(littleEndian); // true or falseTypedArray
https://developer.mozilla.org...
在上面一節中我們使用get和set的方式基于數據類型來讀寫內存(ArrayBuffer)中的數據.
而所謂的TypedArray就是使用類似于操作數組的方式來操作我們的Buffer可以理解為數組中的每一個元素都是不同類型的數據,這樣一來我們可以使用數組上的很多方法,相較于干巴巴的使用get和set更加靈活一些,少掉點頭發.
名字叫做TypedArray的這個對象或者全局構造函數并不存在于JavaScript中.因為類型數組并不只有一個,但是TypedArray代指的這些內容擁有統一的構造函數,統一的屬性統一的方法,不同的只是他們的名字以及所對應的數據類型.
TypedArray()指的是以下的其中之一: Int8Array(); Uint8Array(); Uint8ClampedArray(); Int16Array(); Uint16Array(); Int32Array(); Uint32Array(); Float32Array(); Float64Array();
看到這里我們立馬聯想到了之前DataView上不同的Get和Set,概念是一樣的,不同于ArrayBuffer的是,這里的最小數據單位是數組中的元素,不同類型元素所占用的空間是不同的,但是我們不需要考慮在字節層面上進行控制.
接下來我們利用Int8Array來進行討論:
構造函數
傳入一個數值來表示類型數組中元素的數量
傳入任意一個類型數組在保留其原有的長度上進行數據類型轉換
方法(靜態)
Int8Array.from()通過可迭代對象創建一個類型數組
Int8Array.of()通過可變參數創建一個類型數組
例子:
// 32無符號能表示的最大的數值 占4個字節 const int32 = new Int32Array(1); // 使用length int32[0] = 4294967295; // 8位無符號能表示最大的內容是127 占1個字節 const int8 = new Int8Array(int32); // 使用另外一個類型數組 console.log(int8[0]) // -1 32位轉8位要確保,32位的值在8位的范圍內否則無法保證精度 const from = Int8Array.from([0,127]); console.log(from.length === 2) // true const of = Int8Array.of(0,127); console.log(of.length === 2)// true
屬性(靜態)
TypedArray.BYTES_PER_ELEMENT
TypedArray.length
TypedArray.name
get TypedArray[@@species]
TypedArray.prototype
屬性(實例)
TypedArray.prototype.buffer
TypedArray.prototype.byteLength
TypedArray.prototype.byteOffset
TypedArray.prototype.length
方法(實例)
方法是在是太多了Array上的方法TypedArray基本都有,例舉太多都是照搬MDN,給個貼上大家自行查閱吧.
方法列表
例子(類數組操作):
const int8 = new Int8Array(2); int8[0] = 0; int8[1] = 127; int8.forEach((value)=>console.log(value)); for (const elem of int8) { console.log(elem); } Array.isArray(int8) // false 類數組而不是真的數組Blob
https://developer.mozilla.org...Blob` 對象表示一個不可變、原始數據的類文件對象。Blob 表示的不一定是JavaScript原生格式的數據
這說明了什么意思,類似于ArrayBuffer一樣,ArrayBuffer本身沒有為了達到某種目的而提供具體的操作方法,他的存在就類似于一個占位符一樣,Blob對象也是類似的概念,在JavaScript中我們使用Blob對象來表示一個文件,當這個文件需要進行操作的時候我們在利用其他途徑對這個Blob對象進行操作.(個人理解)
Blob的API和ArrayBuffer非常相似,因為他們有著非常密切的聯系,創建Blob對象有兩種方式,對應著兩種具體的需求:
直接調用構造函數傳入JavaScript中的數據結構
使用File對象創建,用于表示文件
這里我們不討論由File對象創建的情況,這部分留到下節中討論.
構造函數
你可以利用現有的JavaScript數據結構來創建一個Blob對象
你可以選擇這個Blob對象的MIME類型
你可以控制這個Blob對象中的換行符在系統中表現的行為
具體參考
屬性(實例)
size - Blob對象所包含的數據大小
type - Blob對象所描述的MIME類型
方法(實例)
slice()類似于ArrayBuffer.slice()從原有的Blob中分離出一部分組成新的Blob對象
例子:
const blob1 = new Blob([JSON.stringify({ content: "success" })], { type: "application/json" }); const blob2 = new Blob(["hey!"],{ type:"text/html" });
注意:Blob對象接受的第一個參數是一個數組.
Blob對象還可以根據其他數據結構進行創建:
ArrayBuffer
ArrayBufferView(TypedArray)
Blob
https://developer.mozilla.org...
乍一看Blob對象看似很雞肋,不過在JavaScript中能裝載數據還可以指定MIME類型,這種情況多半都是用于和外部進行交互.
回顧前面的內容,我們知道了如何創建一片內存中的區域,還知道了如何利用不同的工具來對這篇內存進行操作,最重要的一個用于描述文件Blob對象接受ArrayBuffer和TypedArray,那么還能玩出什么花樣呢?
File文件(File)接口提供有關文件的信息,并允許網頁中的 JavaScript 訪問其內容。https://developer.mozilla.org...
File對象用于描述文件,這個對象雖然可以利用構造函數自行創建,但是大多數情況下都是利用瀏覽器上的元素或者拖拽API來獲取的.
File對象繼承Blob對象,所以繼承了Blob對象上的原型方法和屬性,和Blob純粹表示文件不同,File更加接地氣一點,他還擁有了我們操作系統上常見的一些特征:
屬性(實例)
lastModified 最后修改時間
name 文件名稱
size 文件大小
type MIME類型
詳細介紹
構造函數
詳細介紹
例子:
// 創建buffer const buffer = new Int8Array(2); console.log(buffer.byteLength); // 2 buffer[0] = 0; buffer[1] = 127 console.log(buffer[0]); // 127 // 利用buffer創建一個file對象 const file = new File([buffer],"text.txt",{ type:"text/plain", lastModified:Date.now() }); // file繼承blob所以可以使用slice方法,返回一個blob對象 const blob = file.slice(1,2,"text/plain"); console.log(blob.size); //1
File對象目前看來依然扮演者"載體"的角色,不過在將他交由其他的API的時候才是他真正發揮威力的地方.
FileReaderFileReader一看名字我就有一種想喊JavaScript(瀏覽器端)永不為奴的沖動.前面鋪墊了那么多終于可以看到真正可以實際利用的內容了.
FileReader 對象允許Web應用程序異步讀取存儲在用戶計算機上的文件(或原始數據緩沖區)的內容,使用 File 或 Blob 對象指定要讀取的文件或數據。https://developer.mozilla.org...
FileReader和前面的所提到的內容不同的地方在于,這個API有事件,你可以使用onXXX和addEventListener進行監聽.
基本工作流程:
獲取用戶提供的文件對象(通過input或者拖拽)
或者自己創建File或者(Blob)對象
新建一個FileReader()實例
監聽對應的方法來獲取讀取內容完成后的回調
利用不同的方法讀取文件內容
讀取為fileReader.ArrayBuffer()
讀取為DataURLfileReader.readAsDataURL()
讀取為字符串fileReader.readAsText()
示例1讀取計算機上的文件:
blob
如果一切順利,你就可以從計算機上讀取一個文件,并且以文本的形式展現在了控制臺中.
而且不僅如此,利用:
reader.readAsArrayBuffer(file)
我們可以讀取任何類型的數據,然后再內存中進行修改,剩下的就差保存了.
FileReaderSync這個API是FileReader的同步版本,這意味著代碼執行到讀取的時候會等待文件的讀取,所以這個API只能在workers里面使用,如果在主線程中調用它會阻塞用戶界面的執行.
由于是同步讀取,所以沒有回調掉必要存在,也就不需要監聽事件了.
https://developer.mozilla.org...URL
前面我們討論完成了數據的讀取,在FileReader中我們已經可以獲取ArrayBuffer然后使用DateView和TypedArray就可以修改ArrayBuffer完成文件的修改,接下來我們旅行中的最后一程.
https://developer.mozilla.org...
在JavaScript(瀏覽器端)中我們可以使用URL來創建一個URL對象:
new URL("https://www.xxx.com?q=10")
他返回的對象包含如下的內容:
// 控制臺 new URL("https://www.xxx.com?q=10") URL hash: "" host: "www.xxx.com" hostname: "www.xxx.com" href: "https://www.xxx.com/?q=10" origin: "https://www.xxx.com" password: "" pathname: "/" port: "" protocol: "https:" search: "?q=10" searchParams: URLSearchParams { } username: ""
可見該對象是一個工具對象用于幫助我們更加容易的處理URL.
例子(來自MDN):
var a = new URL("/", "https://developer.mozilla.org"); // Creates a URL pointing to "https://developer.mozilla.org/" var b = new URL("https://developer.mozilla.org"); // Creates a URL pointing to "https://developer.mozilla.org" var c = new URL("en-US/docs", b); // Creates a URL pointing to "https://developer.mozilla.org/en-US/docs" var d = new URL("/en-US/docs", b); // Creates a URL pointing to "https://developer.mozilla.org/en-US/docs" var f = new URL("/en-US/docs", d); // Creates a URL pointing to "https://developer.mozilla.org/en-US/docs" var g = new URL("/en-US/docs", "https://developer.mozilla.org/fr-FR/toto"); // Creates a URL pointing to "https://developer.mozilla.org/en-US/docs" var h = new URL("/en-US/docs", a); // Creates a URL pointing to "https://developer.mozilla.org/en-US/docs" var i = new URL("/en-US/docs", ""); // Raises a SYNTAX ERROR exception as "/en-US/docs" is not valid var j = new URL("/en-US/docs"); // Raises a SYNTAX ERROR exception as "about:blank/en-US/docs" is not valid var k = new URL("http://www.example.com", "https://developers.mozilla.com"); // Creates a URL pointing to "https://www.example.com" var l = new URL("http://www.example.com", b); // Creates a URL pointing to "https://www.example.com"
實際上這和Node中的URL對象十分相似:
// 終端 > Node > new URL("https://www.xxx.com/?q=10") URL { href: "https://www.xxx.com/?q=10", origin: "https://www.xxx.com", protocol: "https:", username: "", password: "", host: "www.xxx.com", hostname: "www.xxx.com", port: "", pathname: "/", search: "?q=10", searchParams: URLSearchParams { "q" => "10" }, hash: "" }
它和我們討論的文件下載有什么關系呢,在我們在瀏覽器中一切可以利用的資源都有唯一的標識符那就是URL.
而我們自定義或者讀取的文件需要通過URL對象創建一個指向我們定義資源的鏈接.
那么URL對象上提供了兩個靜態方法:
URL.createObjectURL() 創建根據URL或者Blob創建一個URL
URL.revokeObjectURL() 銷毀之前已經創建的URL實例
那么生成的這個URL,可以被用在任何使用URL的地方,在這個例子中我們讀取一個圖片,然后將它賦值給img標簽的src屬性,這會在你的瀏覽器中打開一張圖片.
blob
我們的圖片被如下格式的URL所描述:
blob:http://127.0.0.1:5500/b285f19f-a4e2-48e7-b8c8-5eae11751593導出文件實踐
主要是利用瀏覽器在解析到MIME為application/octet-stream類型的內容會彈出下載對話框的特性.
我們有如下對策:
創建一個File對象修改他的type為application/octet-stream
使用這個File利用URL.createObjectURL()創建一個URL
重定向到這個URL,讓瀏覽器自動彈出下載框
const buffer = new ArrayBuffer(1024), array = new Int8Array(buffer); array.fill(1); const blob = new Blob(array), file = new File([blob],"test.txt",{ lastModified:Date.now(), type:"application/octet-stream" }); saveAs(file,"test.txt") const url = window.URL.createObjectURL(file); window.location.href = url;
上面這種方式簡單粗,不過導出的文件你得修改文件名稱.
我們只需要稍稍利用利用a標簽就可以優雅的完成這項任務:
const buffer = new ArrayBuffer(1024), array = new Int8Array(buffer); array.fill(1); const blob = new Blob(array), file = new File([blob],"test.txt",{ lastModified:Date.now(), type:"text/plain;charset=utf-8" }); const url = window.URL.createObjectURL(file), a = document.createElement("a"); a.href = url; a.download = file.name; // see https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/a#%E5%B1%9E%E6%80%A7 a.click();
大功告成,利用HTML5的API我們終于可以愉快的在WEB上操作數據啦!
MDN上幾篇不錯的指引分別是:
在web應用程序中操作文件指南
JavaScript 類數組對象
Base64的編碼與解碼
參考https://github.com/SheetJS/js...https://github.com/eligrey/Fi...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/53672.html
摘要:高度的控制是自底向上的。因此,要解決這兩個問題,就需要將高度的控制方向顛倒過來高度的控制是自頂向下的。元素的高度是通過自頂向下的方式確定的比如,元素的高度是根據自底向上的方式確定的。 從一個常見問題開始討論:如何用css將一個元素的高度設置為【瀏覽器內容窗口高度】。 方案一:使元素高度占滿屏幕 在css中,vh是一個特殊的長度單位,100vh的值就是【瀏覽器內容窗口高度】。因此heig...
摘要:簡介本篇文章主要探討中的數據操作一直以來給人一種比較低能的感覺例如無法讀取系統上的文件不能做一些底層的操作所以在頁面上操作數據會交由服務器處理也就成了主流的做法但是很多人沒有發現實際上以及在逐步增強這些功能現在我們就已經可以放心的在端進行文 簡介 本篇文章主要探討JavaScript中的數據操作. JavaScript一直以來給人一種比較低能的感覺,例如無法讀取系統上的文件,不能做一些...
摘要:在本文中,筆者主要想分享一下自底向上構建知識圖譜的全過程,拋磚引玉,歡迎大家交流。隨著自動知識抽取與加工技術的不斷成熟,當前的知識圖譜大多采用自底向上的方式構建,如的和微軟的知識庫。 阿里妹導讀:知識圖譜的構建技術主要有自頂向下和自底向上兩種。其中自頂向下構建是指借助百科類網站等結構化數據源,從高質量數據中提取本體和模式信息,加入到知識庫里。而自底向上構建,則是借助一定的技術手段,從...
摘要:上一篇中已經介紹了幾個簡單的排序算法,這一篇文章我將繼續向大家介紹排序算法相關的內容,本篇的會介紹希爾排序快速排序歸并排序以及分治算法的思想,希望通過本文章能夠加深大家對排序算法的理解。 上一篇中已經介紹了幾個簡單的排序算法,這一篇文章我將繼續向大家介紹排序算法相關的內容,本篇的會介紹希爾排序、快速排序、歸并排序以及分治算法的思想,希望通過本文章能夠加深大家對排序算法的理解。 希爾排序...
閱讀 2650·2023-04-26 00:42
閱讀 2799·2021-09-24 10:34
閱讀 3810·2021-09-24 09:48
閱讀 4145·2021-09-03 10:28
閱讀 2576·2019-08-30 15:56
閱讀 2771·2019-08-30 15:55
閱讀 3254·2019-08-29 12:46
閱讀 2243·2019-08-28 17:52