深入理解前端位元組二進位知識以及相關API

来源:https://www.cnblogs.com/jimojianghu/archive/2023/05/10/17292186.html
-Advertisement-
Play Games

當前,前端對二進位數據有許多的API可以使用,這豐富了前端對文件數據的處理能力,有了這些能力,就能夠對圖片等文件的數據進行各種處理。 本文將著重介紹一些前端二進位數據處理相關的API知識,如Blob、File、FileReader、ArrayBuffer、TypeArray、DataView等等。 ...


當前,前端對二進位數據有許多的API可以使用,這豐富了前端對文件數據的處理能力,有了這些能力,就能夠對圖片等文件的數據進行各種處理。
本文將著重介紹一些前端二進位數據處理相關的API知識,如Blob、File、FileReader、ArrayBuffer、TypeArray、DataView等等。

位元組

在介紹各種API之前,我們需要先瞭解下和位元組有關的知識。

我們知道,電腦是二進位的世界,而位元組(byte)是電腦技術中關於二進位數據的一種基本單位,1位元組有8個二進位位,即8比特(bit)。

比特又叫位,一位二進位數據要麼是0、要麼是1,只有兩種狀態,所以1比特有2種狀態。
1位元組有8比特,即8個二進位位,那就能表示 2**8 = 256 種狀態,取值從 00000000 到 11111111。

位元組作為基本單位,在很多地方都被使用,如字元編碼知識,見前文前端需要瞭解的編碼知識

二進位數據在存儲的時候,以位元組為單位,這裡還涉及到一個關於位元組序的知識。

位元組序

位元組序描述的是電腦如何存儲位元組。
因為我們知道,記憶體存儲都有索引地址,每個位元組對應一個索引地址。一個位元組存儲8位二進位,即0到255之間,但需要存儲大於255的數值的時候,就需要多個位元組,多個位元組就涉及到排序問題。
所以位元組序就是:當需要多個位元組表示一個值的時候,這多個位元組使用什麼樣的排序方式在記憶體中進行存儲。
而排序方式主要是兩種:大端存儲(big-endian)和小端存儲(little-endian)。

大端存儲和小端存儲

大端存儲又稱大位元組序、高位元組序,方式是低位位元組排在記憶體中的高地址端,高位元組位排放在記憶體中的低地址端。圖片文件 png、jpg都是這種方式。
小端存儲又稱為小位元組序、低位元組序,方式是低位位元組排在記憶體中的低地址端,高位位元組排在記憶體中的高地址端。圖片文件gif是小端序。

示例

當我們使用不同的位元組序存儲數字 0x12345678 (這裡是16進位表示,對應的十進位:305419896。進位相關知識可見前文Javascript中的進位和進位轉換

大端存儲在記憶體中的存儲地址:
image
小端存儲在記憶體中的存儲地址:
image

這裡數字位元組的高-低位是從左到右,最高位是 12,最低位是 78;而記憶體中存儲時從左到右是低地址——高地址。
所以在大端序中高位位元組的 12 在記憶體最左邊的低地址位,而低位元組位 78 則在記憶體最右邊的高地址位;而小端序則正好相反。

從視覺習慣上,大端存儲似乎更順眼,但無論哪種方式,計算的結果都是一樣的,只是在計算的時候需要處理這個排序方式,下文會涉及到。

Blob

Blob,即 Binary large Object,本質上是一個二進位對象,該對象表示的是一個不可變、原始數據的類文件對象。
它的不可變,代表它是只讀的,不可被改變。

Blob對象的構造函數語法:new Blob(array, options)

參數array:是一個數據數組,可以是多種對象的數據,包含 ArrayBuffer、Blob、String 等等。
參數options:可選對象,指定兩個屬性:

type 表示Blob對象數據的MIME類型;
endings 指定包含行結束符\n的字元串如何寫入。

我們可以使用構造函數直接創建一個新的 Blob 對象:

const blob = new Blob(['123456789'], {type : 'text/plain'});

新創建的對象實例,結構如下:

image

從以上示例,我們就可以看到Blob對象的方法和屬性:

  • 實例屬性
    • size:Blob對象中數據的位元組大小
    • type:字元串,表示Blob對象數據的MIME類型
  • 示例方法
    • arrayBuffer():返回包含Blob所有內容的二進位格式的ArrayBuffer的一個promise對象
    • stream():返回能讀取Blob的ReadableStream對象
    • text():返回包含Blob所有內容的字元串(UTF-8編碼)的一個promise對象
    • slice([start [, end [, contentType]]]):
      • 該方法有三個可選參數,可用於分割Blob數據
      • 它根據指定的起始和結束位置,返回原Blob在該範圍的數據,得到一個新的Blob對象
      • 第三個參數 contentType 可以為新Blob對象指定自己的MIME類型

可以針對上面的 blob 實例進行操作:

blob.slice(0, 3).text().then(res => {
  console.log(res)
})
// 結果:123

以上代碼,使用slice()方法獲取原blob的前三位的數據,生成新的Blob實例後,通過text()方法列印出文本內容。

下麵可以看看Blob在介面請求中的應用,Fetch API中的 Response 對象,擁有一個blob方法,能夠得到Blob對象。

const imgRequst = new Request('11.jpg')
fetch(imgRequst).then((response) => {
  return response.blob()
}).then((mBlob) => {
  console.log(mBlob)
})

通過以上代碼,請求一個jpg圖片文件,響應對象通過 blob() 方法轉為Blob對象:

image

File

File對象繼承了Blob對象,是一種特殊類型的Blob,它擴展了對系統文件的支持能力。
File提供文件信息,並能夠在javascript中進行訪問,一般在使用 <input> 標簽選擇文件時返回,因為 <input> 標簽允許選擇多個文件,這裡返回的是文件列表 files

除了 <input> 標簽以外,還有兩種方式返回File對象:

  • 自由拖放操作生成的 DataTransfer 對象。
  • 文件系統訪問API中的 FileSystemFileHandle 對象的 getFile() 方法。

File的構造函數:new File(bits, name[, options])
有三個參數:

  • bits:是一個數據數組,可以是多種對象的數據,與Blob對象類似
  • name:文件名稱
  • options:可選屬性對象,包含兩個選項
    • type:MIME類型字元串
    • lastModified:時間戳,表示文件的最後修改時間

下麵代碼,通過 <input> 標簽讀取文件:

<input id="input-file" type="file" accept="image/*" />
document.getElementById('input-file').onchange = (e) => {
  const file = e.target.files[0]
  console.log(file)
  // ...
}

這是一個簡單的圖片上傳,獲取到的file實例,控制台列印出來:

image

通過上圖(chrome瀏覽器下),可以看到File繼承了Blob的素有屬性和方法:

  • 屬性除了size和type以外,File還有自己的幾個屬性
    • lastModified:只讀,時間戳,文件最後修改時間
    • name:只讀,文件名
      lastModifiedDate:只讀,文件最後修改時間的 Date 對象,該對象已廢棄
    • webkitRelativePath:非標準屬性,返回path或URL
  • File沒有自己的實例方法,都繼承自Blob

對Blob和File的讀取

File繼承自Blob,都是只讀對象,除了使用slice分片以外,並沒有其他操作能力,所以如果對它們進行處理需要藉助其他的API。
主要用於操作Blob的API有:FileReader、URL.createObjectURL()、createImageBitmap()和XMLHttpRequest.send()。下麵將介紹這幾種方式。

Blob和File都是 WebAPI,是由瀏覽器環境提供的,而上面提到這四種對象也同樣是WebAPI。

FileReader

FileReader是用於非同步讀取文件類型(或原始數據緩衝區)的內容,指定Blob或File對象為需要讀取的文件數據。

FileReader 不能在文件系統中用路徑名的方式讀取文件。

構造函數:new FileReader()

如果對文件處理功能開發較多,對FileReader對象應該較熟,我們先看一個示例:

document.getElementById('input-file').onchange = (e) => {
  const file = e.target.files[0]
  const reader = new FileReader()
  reader.onload = async (event) => {
    const img = new Image()
    img.src = event.target.result
  }
  reader.readAsDataURL(file)
}

以上代碼,就是很常用的,使用FileReader讀取一個圖片文件的Base64數據,然後使用圖片對象載入。Base64知識,可參考前文深入理解Base64編碼字元串
這段代碼也涉及到FileReader對像的屬性、事件、方法。

FileReader的屬性事件和方法
  • 屬性(皆只讀)
    • error:在讀取文件時發生的錯誤
    • readyState:表示當前讀取狀態
      常量名 狀態描述
      EMPTY 0 沒有載入
      LOADING 1 正在載入
      DONE 2 已完成全部讀取
    • result:文件內容,讀取狀態完成時才有效
  • 方法
    • abort():中止讀取操作。在返回時,readyState屬性為DONE
    • readAsArrayBuffer():以ArrayBuffer類型讀取Blob中的內容
    • readAsBinaryString():以原始二進位數據類型讀取Blob中的內容
    • readAsDataURL():以Base64字元串類型讀取Blob中的內容
    • readAsText():以文本字元串類型讀取Blob中的內容
  • 事件
    • onabort:讀取操作被中斷時觸發
    • onerror:讀取操作發生錯誤時觸發
    • onload:讀取操作完成時觸發
    • onloadstart:讀取操作開始時觸發
    • onloadend:讀取操作結束時觸發
    • onprogress:讀取Blob時觸發

URL.createObjectURL()

URL是瀏覽器環境提供的,用於處理url鏈接的一個介面對象。可以通過它,解析、構造、規範和編碼各種url鏈接。
而URL提供的一個靜態方法 createObjectURL(),可以用來處理Blob和File文件對象。

先看一個例子:

document.getElementById('input-file').onchange = (e) => {
  const file = e.target.files[0]
  const url = URL.createObjectURL(file)
  const img = new Image()
  img.onload = () => {
    document.body.append(img)
  }
  img.src = url
}

頁面展示:

image

這段代碼就實現了上傳圖片,通過 URL.createObjectURL 讀取後生成一個本地映射的url,再使用Image對象載入圖片。
通過查看頁面元素,可以看到新添加的圖片元素,它的src是一個類似鏈接的字元串:blob:http://localhost:8088/29c8f4a5-9b47-436f-8983-03643c917f1c,通過這個字元串,圖片就能載入顯示出來。
再來看 createObjectURL(),它返回一個包含給定的Blob或File對象的url,就可以當做文件資源被載入。而這個url的生命周期和它的視窗同步,視窗關閉這個url就自動釋放了。

這個url就是被稱為偽協議的Objct URL。

Object URL

Object URL 又被稱為Blob URL,一般使用Blob或File對象生成,通過 URL.createObjectURL() 方法創建一個唯一的URL。
Object URL的格式為:blob:origin/唯一標識(uuid)

上面生成的URL字元串就符合這個格式:blob:http://localhost:8088/29c8f4a5-9b47-436f-8983-03643c917f1c

  • origin 對應的 http://localhost:8088/,如果直接打開本地html文件,則origin為null。
  • uuid 對應 29c8f4a5-9b47-436f-8983-03643c917f1c

瀏覽器內部會為生成Object URL保持一個 URLBlob 的映射,Blob是留存在記憶體中,瀏覽器只有在卸載當前視窗文檔時才會釋放。

如果要手動釋放,則需要URL的另外一個靜態方法:URL.revokeObjectURL(),它用於銷毀之前創建的URL實例,在合適的時機調用即可銷毀Object URL。

URL.revokeObjectURL(url)

XMLHttpRequest.send()

XMLHttpRequest.send(body):用於在XHR的HTTP請求中,發送數據體。
這裡的body參數,可以是多種數據類型,包括Blob對象。

const xhr = new XMLHttpRequest()
xhr.send(new Blob())

createImageBitmap()

createImageBitmap(): 主要處理圖片資源,接受不同的圖片資源對象為參數,並生成一個ImageBitmap對象。
這些參數就就可以是Blob和File對象。

ImageBitmap表示可以繪製在canvas上的點陣圖圖像。

createImageBitmap(file).then(imageBitmap => {
  const canvas = document.createElement('canvas')
  canvas.width = imageBitmap.width
  canvas.height = imageBitmap.height
  const ctx = canvas.getContext('2d')
  ctx.drawImage(imageBitmap, 0, 0)
  document.body.append(canvas)
})

如上代碼,即可讀取圖片文件,使用canvas繪製。

ArrayBuffer

ArrayBuffer 對象表示通用的、固定長度的原始二進位緩衝區,它是一個位元組數組,但不能直接操作它的內容,而需要通過其他方式(如TypeArray或DataView等)進行處理。

構造函數:new ArrayBuffer(length),返回一個指定大小的ArrayBuffer對象。
參數length:要創建的 ArrayBuffer 的位元組大小。大於Number.MAX_SAFE_INTEGER(>= 2 ** 53)或為負數,則拋出一個RangeError異常。

下麵我們先使用前面介紹的 FileReader 讀取一個文件的ArrayBuffer內容:

document.getElementById('input-file').onchange = (e) => {
  const file = e.target.files[0]
  const reader = new FileReader()
  reader.onload = async (event) => {
    console.log(event.target.result)
  }
  reader.readAsArrayBuffer(file)
}

控制台日誌列印輸出:

image

從上圖,可以看到ArrayBuffer的實例屬性和方法:

  • byteLength:表示位元組大小,不可改變
  • slice(begin[, end]):根據指定位置範圍返回一個新的ArrayBuffer,可以分割ArrayBuffer。

ArrayBuffer還有靜態屬性和方法:

  • ArrayBuffer.length:構造函數的length屬性,值為1
  • ArrayBuffer.isView(arg):如果參數是ArrayBuffer的視圖實例則返回true。

由於我們無法直接操作ArrayBuffer,所以需要使用其他對象來處理,下麵將介紹其中兩種。

TypeArray

TypeArray,即類型化數組,它描述了二進位數據緩衝區的一個類數組。TypeArray本身不是一個可用的對象,只是一個輔助的數據類型,作為所有類型數組的構造原型,真正可用的類型數組包含了多種,如Int8Array、Uint8Array等。
常用的類型數組如下表所示:

對象 元素所占位元組數 取值範圍 描述
Int8Array 1 -128 - 127 8 位有符號整型數組
Uint8Array 1 0 - 255 8 位無符號整型數組
Uint8ClampedArray 1 0 - 255 8 位無符號整型固定數組
Int16Array 2 -32768 - 32767 16 位有符號整型數組
Uint16Array 2 0 - 65535 16 位無符號整型數組
Int32Array 4 -2147483648 - 2147483647 32 位有符號整型數組
Uint32Array 4 0 - 4294967295 32 位無符號整型數組
Float32Array 4 1.2×10**-38 to 3.4×10**38 32 位浮點數型數組
Float64Array 8 5.0×10**-324 to 1.8×10**308 64 位浮點數型數組
BigInt64Array 8 -2**63 to 2**63-1 64 位有符號數型數組
BigUint64Array 8 0 to 2**64-1 64 位無符號整型數組

類型化數組與普通數據也較相似,同樣擁有一系列的方法和屬性,但不支持 pushpopshiftunshiftsplice 等可以改變原數組的增刪改方法。
類型化數組由於定義了數據類型,則各元素必須是同類型的數據,不能像普通數據那樣元素可以是不同類型;當元素數據類型固定統一時,處理效率更優。

各類型數組在構造函數、屬性、方法等語法上相同,下麵就以 Uint8Array 為例。

語法

Uint8Array構造函數:

new Uint8Array()
new Uint8Array(length)
new Uint8Array(typedArray)
new Uint8Array(object)
new Uint8Array(buffer [, byteOffset [, length]])

length 參數的最大取值

8 位類數組是 2145386496
16 位類數組是 1072693248
32 位類數組是 536346624
32 位類數組是 268173312

靜態屬性和方法

  • BYTES_PER_ELEMENT:返回數組元素所占位元組數,Uint8Array中的值是1,Uint32Array中的值是4,見上表
  • length:固定長度,Uint8Array中的值是1,Uint32Array中的值是3,基本沒用
  • name:類型數組返回自己的構造名,Uint8Array類型返回 Uint8Array,Uint32Array類型返回 Uint32Array 等等
  • from(source[, mapFn[, thisArg]]):從源類型數組中返回一個新的數組
  • of(element0[, element1[, ...[, elementN]]]):創建一個具有可變數量參數的新類型數組

實例屬性和方法

介紹完靜態屬性和方法,下麵通過一個示例,來查看下Uint8Array的實例屬性和方法,代碼如下。

const reader = new FileReader()
reader.onload = async (event) => {
  const aBuffer = event.target.result
  const uint8Array = new Uint8Array(aBuffer)
  console.log(uint8Array)
}
reader.readAsArrayBuffer(file)

以上代碼,直接讀取文件的ArrayBuffer數據,然後通過 Uint8Array 構造函數,得到Uint8Array實例,控制台查看:

image

通過載入一張png圖片,得到它的Uint8Array數組數據,可以看到類型數組大部分的屬性和方法都和普通數組類似,除了前文提到的增刪改數組的方法以外。因此,對類型數組使用下標、迴圈等等方式進行讀取,和普通函數沒什麼兩樣。

而類型數組也自己的特殊屬性(都只讀)和方法,如下:

  • buffer:返回類型數組引用的ArrayBuffer
  • byteLength:位元組數長度
  • byteOffset:相對源ArrayBuffer的偏移位元組數
  • length:數組長度
  • set(array[, offset]):從給定數組中讀取元素值,並存儲在類型數組中
  • subarray(begin, end):給定開始和結尾索引,返回一個新的類型數組

類型數組間的關係

要瞭解常見類型數組間的關係,我們先看下麵這張圖:

image

圖上所示,是一張png圖片的ArrayBuffer數據,可以看到,ArrayBuffer的位元組長度屬性預設取8位整型數組的長度,即與Int8Array和Uint8Array的長度一致。
而Int8Array的長度29848,正好是Int16Array的長度14924的兩倍,是Int32Array的長度7462的四倍,可知,這裡就是對位元組的合併計算:

  • Int8Array(Uint8Array) 轉 Int16Array(Uint16Array),需要依序合併兩個位元組後計算數值。
  • Int8Array(Uint8Array) 轉 Int32Array(Uint32Array),需要依序合併四個位元組後計算數值。
  • Int16Array(Uint16Array) 轉 Int32Array(Uint32Array),需要依序合併兩個位元組後計算數值。

讀取GIF文件示例

類型數組通過數組的方式對ArrayBuffer的內容進行讀取操作,可以方便我們處理文件的二進位數據。
但使用類型數組的時候,碰到多位元組的數據時,需要考慮位元組序的問題。

下麵,我們以讀取小端存儲的GIF圖片為例。

GIF圖片的Uint8Array數組數據中,寬高數據的存儲就是使用了兩個位元組,第7-8位存儲圖片的寬度,9-10位存儲圖片的高度。

我們載入的GIF圖片寬高皆為600,需要處理位元組序,代碼如下:

const uint8Array = new Uint8Array(aBuffer)
let bufferIndex = 6
// 獲取GIF寬度的兩個位元組的值
const width1 = uint8Array[bufferIndex]
// width1 結果:88
const width2 = uint8Array[bufferIndex + 1]
// width2 結果:2

// 得到各自的16進位數據
const width1hex = width1.toString(16)
const width2hex = width2.toString(16)
// 轉換成實際的寬度大小,註意這裡把兩個位元組的順序做了調整,符合小端序
const width = parseInt(width2hex + width1hex, 16)
// width 結果:600

使用小端序處理後,寬度結果等於600,符合圖片實際寬度。
自己手動處理位元組序會稍顯麻煩,如果不想手動去處理位元組序的問題,可以使用另外一個對象:DataView

DataView

DataView 是一個從 ArrayBuffer 中讀取多種類型數值並且不用考慮位元組序的介面對象。它的使用簡單方便,擁有一系列的 get-set- 實例方法操作數據。

DataView的構造函數:new DataView(buffer [, byteOffset [, byteLength]])
參數:

  • buffer:源ArrayBuffer
  • byteOffset:buffer中的位元組偏移量
  • byteLength:位元組長度

DataView不用考慮位元組序,同樣是讀取GIF的寬度時,代碼可簡化:

const fileDataView = new DataView(arrBuffer)
let bufferIndex = 6
const width = fileDataView.getUint16(bufferIndex, true)
// 結果:600
bufferIndex += 2
const height = fileDataView.getUint16(bufferIndex, true)
// 結果:600

以上代碼,很方便就得到GIF圖片的寬高數據(600),因為使用了 DataView 和它的 getUint16 方法,不需要手動處理位元組序。
getUint16 方法有兩個參數:第一個參數代表位元組索引;第二參數表示位元組序,預設大端序,為true則是小端序,GIF是小端,所以上面代碼為true。
除了getUint16以外,DataView 還有十多個類似的實例方法。

DataView的get和set系列方法

get系列方法通過位元組偏移索引獲取對應的數值,其中多位元組的數據,需要兩個參數:

  • byteOffset:讀取時的位元組偏移量
  • littleEndian:位元組序,預設大端,設為true則是小端
名稱 參數 描述
getInt8 (byteOffset) 有符號 8-bit 整數(1個位元組)
getUint8 (byteOffset) 無符號 8-bit 整數(1個位元組)
getInt16 (byteOffset [, littleEndian]) 16-bit數(短整型,2個位元組)
getUint16 (byteOffset [, littleEndian]) 16-bit數(無符號短整型,2個位元組)
getInt32 (byteOffset [, littleEndian]) 32-bit數(長整型,4個位元組)
getUint32 (byteOffset [, littleEndian]) 32-bit數(無符號長整型,4個位元組)
getFloat32 (byteOffset [, littleEndian]) 32-bit浮點數(單精度浮點數,4個位元組)
getFloat64 (byteOffset [, littleEndian]) 64-bit數(雙精度浮點型,8個位元組)
getBigInt64 (byteOffset [, littleEndian]) 帶符號的64位整數(long long類型)值
getBigUint64 (byteOffset [, littleEndian]) 無符號的64位整數(unsigned long long類型)值

set系列方法是和get方法對應的,處理相應位元組偏移索引位置的數值,參數如下:

  • byteOffset:讀取時的位元組偏移量
  • value:設置相應類型的數值
  • littleEndian:位元組序,預設大端,設為true則是小端
名稱 參數 描述
setInt8 (byteOffset, value) 8-bit數(一個位元組)
setUint8 (byteOffset, value) 8-bit數(無符號位元組)
setInt16 (byteOffset, value [, littleEndian]) 16-bit數(短整型)
setUint16 (byteOffset, value [, littleEndian]) 16-bit數(無符號短整型)
setInt32 (byteOffset, value [, littleEndian]) 32-bit數(長整型)
setUint32 (byteOffset, value [, littleEndian]) 32-bit數(無符號長整型)
setFloat32 (byteOffset, value [, littleEndian]) 32-bit數(浮點型)
setFloat64 (byteOffset, value [, littleEndian]) 64-bit數(雙精度浮點型)
setBigInt64 (byteOffset, value [, littleEndian]) 帶符號的64位整數(long long類型)值
setBigUint64 (byteOffset, value [, littleEndian]) 無符號的64位整數(unsigned long long類型)值

Blob和ArrayBuffer

對於Blob和ArrayBuffer兩個對象,我們可以稍做總結:

  1. Blob是Web API,瀏覽器環境提供,讀取它可以使用FileReader、URL.createObjectURL等WebAPI;ArrayBuffer是JS語言內置對象,處理它則需要使用TypeArray、DataView等JS-API。
  2. Blob表示不可變的類文件數據;ArrayBuffer則表示原始數據緩衝區。
  3. Blob用於讀取類文件數據,不對應記憶體;ArrayBuffer用於讀取記憶體數據。
  4. Blob和ArrayBuffer都需要通過其他對象才能操作數據。
  5. Blob和ArrayBuffer可以使用不同方式進行相互之間的轉換。
  6. 要操作位元組二進位數據,得依賴ArrayBuffer和輔助它的操作對象。

Blob和ArrayBuffer之間的轉換:

  • 使用Blob構造函數可以讀取ArrayBuffer,生成一個新的Blob。
  • 通過Blob實例的arrayBuffer()方法,可以獲取到對應的ArrayBuffer。
  • 通過FileReader對象的readAsArrayBuffer()方法,將Blob讀取為ArrayBuffer。

如下代碼:

const aBuffer = new ArrayBuffer(4)
// 使用Blob構造函數
const blob = new Blob([aBuffer])
// Blob的arrayBuffer()方法(promise)
blob.arrayBuffer()
// FileReader
const reader = new FileReader()
reader.readAsArrayBuffer(blob)

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • (如何從800萬數據中快速撈出自己想要的數據) 一、需求調研 正如題目所說,我們使用的是Oracle資料庫,數據量在800萬左右。我們要完成的事情就是在著800萬數據中,通過某些欄位進行模糊查詢,得到我們所需要的結果集。 這是表裡的數據,一共7328976 條數據,接近800萬 select cou ...
  • 摘要:本文分析了分散式資料庫發展情況、分散式資料庫應用的主要問題,從行業應用的角度給出了分散式資料庫發展的建議。 本文分享自華為雲社區《數字化轉型下我國分散式資料庫應用挑戰及發展建議》,作者:資料庫領域科學家、華為雲資料庫GaussDB首席專家 馮柯。 當前,金融等重點行業都在進行數字化轉型,而分佈 ...
  • 1 麻煩的地方 在SQL Server的官方文檔裡面可以看到備份和還原的表,但是這些表裡面只能找到備份成功的相關信息,無法找到備份失敗的記錄,比如msdb.dbo.backupset。對於一些監控系統未監控作業的情況下,想要監控資料庫備份任務執行失敗而觸發告警規則,有些麻煩。 但是SQL serve ...
  • 源碼地址:https://ext.dcloud.net.cn/plugin?id=12272 ...
  • 😊在Nuxt3.0項目中用到了可視化圖表📊,於是我用了EChart可視化圖表庫。但是在官網我沒有找到針對在Nuxt3.0中使用EChart的方法,於是在這裡記錄我的引入EChart並簡單使用的步驟。需要聲明的是,本文只針對在Nuxt3.0項目中使用EChart.js庫的可視化圖表進行講解,不針對 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 先上效果 前言 最近在學Three.js.,對著文檔看了一周多,正好趕上碼上掘金的活動,就順便寫了一個小demo,手搓一個羅盤特效。 太極 先來看一下太極的實現方式,這裡我們使用CircleGeometry,將其分解開來可以看出是由圓形和 ...
  • 我們是袋鼠雲數棧 UED 團隊,致力於打造優秀的一站式數據中台產品。我們始終保持工匠精神,探索前端道路,為社區積累並傳播經驗價值。。 本文作者:奇銘(掘金) 需求背景 前段時間,離線計算產品接到改造數據同步表單的需求。 一方面,數據同步模塊的代碼可讀性和可維護性比較差,相對應的,在數據同步模塊開發新 ...
  • <meta> 標簽是 HTML 中用於描述網頁元信息的元素。它位於 <head> 部分,不會顯示在頁面內容中,但對於瀏覽器、搜索引擎等具有重要作用。主要作用有:定義文檔的字元編碼、提供網頁的描述信息、關鍵詞、作者、視口設置等,這些信息有助於搜索引擎理解和索引網頁內容。 <meta> 標簽的主要屬性有 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...