Skip to content
索引

文件二进制

mdn

Blob

专门用于支持文件操作的二进制对象,第一个参数是typedArray二维数组,第二个参数是 MIME 类型

js
const blob = new Blob([u8Arr], { type: "application/pdf"})
const blob = new Blob([u8Arr], { type: "application/pdf"})

File

文件(File)对象提供有关文件的信息,并允许网页中的 JavaScript 访问其内容

通常情况下, File 对象是来自用户在一个input元素上选择文件后返回的 FileList 对象,也可以是来自由拖放操作生成的 DataTransfer 对象,或者来自 HTMLCanvasElement 上的 mozGetAsFileAPI

Blob 是最原始的文件对象,File 是基于 Blob 改造的,File继承了所有Blob的属性方法,所以File对象可以看作一种特殊的Blob对象

FileReader, URL.createObjectURL(), createImageBitmap() (en-US), 及 XMLHttpRequest.send() 都能处理 Blob File

示例-浏览器预览本地pdf文件

vue
<template>
  <input type="file" @change="update" />
</template>

<script lang="ts" setup>
function update(e: any) {
  const file = e.target.files[0]
  const reader = new FileReader()
  reader.readAsDataURL(file)
  reader.onload = res => {
    const base64 = res.target!.result as string
    const bstr = atob(base64.substring(base64.indexOf(",") + 1))
    let n = bstr.length
    const u8arr = new Uint8Array(n)
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n)
    }
    const blob = new Blob([u8arr], { type: "application/pdf" })
    const a = document.createElement("a")
    const url = URL.createObjectURL(blob)
    a.href = url
    // 下载的话加一句
    // a.download = "name"
    a.click()
    URL.revokeObjectURL(url)
  }
}
</script>
<template>
  <input type="file" @change="update" />
</template>

<script lang="ts" setup>
function update(e: any) {
  const file = e.target.files[0]
  const reader = new FileReader()
  reader.readAsDataURL(file)
  reader.onload = res => {
    const base64 = res.target!.result as string
    const bstr = atob(base64.substring(base64.indexOf(",") + 1))
    let n = bstr.length
    const u8arr = new Uint8Array(n)
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n)
    }
    const blob = new Blob([u8arr], { type: "application/pdf" })
    const a = document.createElement("a")
    const url = URL.createObjectURL(blob)
    a.href = url
    // 下载的话加一句
    // a.download = "name"
    a.click()
    URL.revokeObjectURL(url)
  }
}
</script>

FileReader

FileReader 对象允许web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 FileBlob 对象指定要读取的文件或数据

其中File对象可以是来自用户在一个<input>元素上选择文件后返回的FileList对象,也可以来自拖放操作生成的 DataTransfer对象,还可以是来自在一个HTMLCanvasElement上执行mozGetAsFile()方法后的返回结果

构造函数

创建FileReader对象

js
let reader = new FileReader()
let reader = new FileReader()

APi

方法名参数描述
readAsBinaryStringfile将文件读取为二进制编码
readAsTextfile,[encoding]将文件读取为文本
readAsDataURLfile将文件读取为DataURL,base64
abort(none)终端读取操作

事件触发

事件触发时机
onabort当读取操作被中止时调用
onerror当读取操作发生错误时调用
onload当读取操作成功完成时调用
onloadend当读取操作完成时调用,不管是成功还是失败.该处理程序在onload或者onerror之后调用
onloadstart当读取操作将要开始之前调用
onprogress在读取数据过程中周期性调用

示例-读取txt

vue
<template>
  <input type="file" @change="readTxt" />
  <p>{{ txt }}</p>
</template>

<script lang="ts" setup>
const txt = ref("")
function readTxt(e: any) {
  const reader = new FileReader()
  reader.readAsText(e.target.files[0], "UTF-8")
  reader.onload = e => {
    txt.value = e.target?.result as string
  }
}
</script>
<template>
  <input type="file" @change="readTxt" />
  <p>{{ txt }}</p>
</template>

<script lang="ts" setup>
const txt = ref("")
function readTxt(e: any) {
  const reader = new FileReader()
  reader.readAsText(e.target.files[0], "UTF-8")
  reader.onload = e => {
    txt.value = e.target?.result as string
  }
}
</script>

示例-选择图片浏览器中预览

js
<template>
  <div>
    <input type="file" @change="readImg" />
    <img :src="imgSrc" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      imgSrc: ""
    }
  },
  methods: {
    readImg(e) {
      const reader = new FileReader()
      const file = e.target.files[0]
      reader.readAsDataURL(file)
      reader.onload = e => {
        this.imgSrc = e.target.result
      }
    }
  }
}
</script>
<template>
  <div>
    <input type="file" @change="readImg" />
    <img :src="imgSrc" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      imgSrc: ""
    }
  },
  methods: {
    readImg(e) {
      const reader = new FileReader()
      const file = e.target.files[0]
      reader.readAsDataURL(file)
      reader.onload = e => {
        this.imgSrc = e.target.result
      }
    }
  }
}
</script>

示例-选择pdf浏览器中预览

vue
<template>
  <input type="file" @change="readImg" />
</template>

<script lang="ts" setup>
function readImg(e: any) {
  const reader = new FileReader()
  const base64 = e.target.files[0]
  reader.readAsDataURL(base64)
  reader.onload = e => {
    blobToFile(e.target?.result as string)
  }
}

const base64ToBlob = function (base64: string) {
  let bstr = atob(base64.split(",")[1])
  let l = bstr.length
  let u8Arr = new Uint8Array(l)
  while (l--) {
    u8Arr[l] = bstr.charCodeAt(l)
  }
  return new Blob([u8Arr], {
    type: "application/pdf"
  })
}
// blob转file
const blobToFile = function (base64: string) {
  const blob = base64ToBlob(base64)
  let fileURL = URL.createObjectURL(blob)
  window.open(fileURL)
}
</script>
<template>
  <input type="file" @change="readImg" />
</template>

<script lang="ts" setup>
function readImg(e: any) {
  const reader = new FileReader()
  const base64 = e.target.files[0]
  reader.readAsDataURL(base64)
  reader.onload = e => {
    blobToFile(e.target?.result as string)
  }
}

const base64ToBlob = function (base64: string) {
  let bstr = atob(base64.split(",")[1])
  let l = bstr.length
  let u8Arr = new Uint8Array(l)
  while (l--) {
    u8Arr[l] = bstr.charCodeAt(l)
  }
  return new Blob([u8Arr], {
    type: "application/pdf"
  })
}
// blob转file
const blobToFile = function (base64: string) {
  const blob = base64ToBlob(base64)
  let fileURL = URL.createObjectURL(blob)
  window.open(fileURL)
}
</script>

Base64

Base64 是一组相似的二进制到文本(binary-to-text)的编码规则,使得二进制数据在解释成 radix-64 的表现形式后能够用 ASCII 字符串的格式表示出来。Base64 这个词出自一种 MIME 数据传输编码

Base64编码普遍应用于需要通过被设计为处理文本数据的媒介上储存和传输二进制数据而需要编码该二进制数据的场景。这样是为了保证数据的完整并且不用在传输过程中修改这些数据。Base64 也被一些应用(包括使用 MIME 的电子邮件)和在 XML (en-US) 中储存复杂数据时使用。

在 JavaScript 中,有两个函数被分别用来处理解码和编码 base64 字符串:

atob 函数能够解码通过base-64编码的字符串数据。相反地,btoa 函数能够从二进制数据字符串创建一个base64编码的ASCII字符串

atob()btoa() 均使用字符串

可以直接作为img图片的src路径

blob转base64

js
  const reader = new FileReader()
  reader.readAsDataURL(blob)
  reader.onload = e => {
    console.log(e.target.result)
  }
  const reader = new FileReader()
  reader.readAsDataURL(blob)
  reader.onload = e => {
    console.log(e.target.result)
  }

ArrayBuffer

先来了解一个专业名词,视图(view),指的是对同一段内存,采取不同的解读方式,所以视图的作用就是以指定格式解读二进制数据,如:

js
// 创建一个8字节的ArrayBuffer
const b = new ArrayBuffer(8)
// 创建一个指向b的Int32视图,开始于字节0,直到缓冲区的末尾
const v1 = new Int32Array(b)
// 创建一个指向b的Uint8视图,开始于字节2,直到缓冲区的末尾
const v2 = new Uint8Array(b, 2)
// 创建一个指向b的Int16视图,开始于字节2,长度为2
const v3 = new Int16Array(b, 2, 2)
// 创建一个8字节的ArrayBuffer
const b = new ArrayBuffer(8)
// 创建一个指向b的Int32视图,开始于字节0,直到缓冲区的末尾
const v1 = new Int32Array(b)
// 创建一个指向b的Uint8视图,开始于字节2,直到缓冲区的末尾
const v2 = new Uint8Array(b, 2)
// 创建一个指向b的Int16视图,开始于字节2,长度为2
const v3 = new Int16Array(b, 2, 2)

ArrayBuffer对象是固定长度的二进制数据缓冲区,即一段内存,它不能直接读写,只能通过视图(浏览器端TypedArray视图和DataView视图 , nodejs中特有视图Buffer)来读写

ArrayBuffer对象作为内存区域,可以存放多种类型的数据

ArrayBuffer有两种视图,一种是TypedArray视图,另一种是DataView视图。前者的数组成员都是同一个数据类型,后者的数组成员可以是不同的数据类型

ArrayBuffer也是一个构造函数,可以分配一段可以存放数据的连续内存区域

js
const buf = new ArrayBuffer(32)
// Uint8Array属于TypedArray视图
const view = new Uint8Array(32)
const buf = new ArrayBuffer(32)
// Uint8Array属于TypedArray视图
const view = new Uint8Array(32)
  1. ArrayBuffer存放0和1组成的二进制数据
  2. 数组放在堆中,ArrayBuffer则把数据放在栈中,所以取数据时更快
  3. ArrayBuffer初始化后固定大小,数组则可以自由增减

TypedArray

目前,TypedArray视图一共包括 9 种类型,每一种类型都是一种构造函数

  • Int8Array:8 位有符号整数,长度 1 个字节
  • Uint8Array:8 位无符号整数,长度 1 个字节
  • Uint8ClampedArray:8 位无符号整数,长度 1 个字节,溢出处理不同
  • Int16Array:16 位有符号整数,长度 2 个字节
  • Uint16Array:16 位无符号整数,长度 2 个字节
  • Int32Array:32 位有符号整数,长度 4 个字节
  • Uint32Array:32 位无符号整数,长度 4 个字节
  • Float32Array:32 位浮点数,长度 4 个字节
  • Float64Array:64 位浮点数,长度 8 个字节

这 9 个构造函数生成的数组,统称为TypedArray视图。它们很像普通数组,都有length属性,都能用方括号运算符([])获取单个元素,所有数组的方法,在它们上面都能使用。普通数组与 TypedArray 数组的差异主要在以下方面。

  • TypedArray 数组的所有成员,都是同一种类型
  • TypedArray 数组的成员是连续的,不会有空位
  • TypedArray 数组成员的默认值为 0。比如,new Array(10)返回一个普通数组,里面没有任何成员,只是 10 个空位;new Uint8Array(10)返回一个 TypedArray 数组,里面 10 个成员都是 0
  • TypedArray 数组只是一层视图,本身不储存数据,它的数据都储存在底层的ArrayBuffer对象之中,要获取底层对象必须使用buffer属性
js
// 创建一个8字节的ArrayBuffer
const b = new ArrayBuffer(8)
// 创建一个指向b的Int32视图,开始于字节0,直到缓冲区的末尾
const v1 = new Int32Array(b)
// 创建一个指向b的Uint8视图,开始于字节2,直到缓冲区的末尾
const v2 = new Uint8Array(b, 2)
// 创建一个指向b的Int16视图,开始于字节2,长度为2
const v3 = new Int16Array(b, 2, 2)
// 创建一个8字节的ArrayBuffer
const b = new ArrayBuffer(8)
// 创建一个指向b的Int32视图,开始于字节0,直到缓冲区的末尾
const v1 = new Int32Array(b)
// 创建一个指向b的Uint8视图,开始于字节2,直到缓冲区的末尾
const v2 = new Uint8Array(b, 2)
// 创建一个指向b的Int16视图,开始于字节2,长度为2
const v3 = new Int16Array(b, 2, 2)
  • 第一个参数(必需):视图对应的ArrayBuffer对象
  • 第二个参数(可选):视图开始的字节序号,默认从 0 开始
  • 第三个参数(可选):视图包含的数据个数,默认直到本段内存区域结束

Uint8Array

TypedArray视图的九种类型之一

Uint8Array 数组类型表示一个8位无符号整型数组,创建时内容被初始化为0。创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素

js
const uint8 = new Uint8Array(2)
uint8[0] = 42
console.log(uint8[0]) // 42
console.log(uint8.length) // 2
const uint8 = new Uint8Array(2)
uint8[0] = 42
console.log(uint8[0]) // 42
console.log(uint8.length) // 2

普通数组转Uint8Array

js
const arr = [1,2,3]
const uint8 = new Uint8Array(arr)
const arr = [1,2,3]
const uint8 = new Uint8Array(arr)

Buffer

Buffer是Node.JS中特有的一种视图(浏览器没有),与TypedArray视图和DataView视图并驾齐驱

js
let buffer = new Buffer(arraybuffer)
let buffer = new Buffer(arraybuffer)

charCodeAt

返回字符串第一个字符的 Unicode 编码(H 的 Unicode 值)

js
const str = "HELLO WORLD"
const n = str.charCodeAt(0)
const str = "HELLO WORLD"
const n = str.charCodeAt(0)

浏览器使用jspdf导出pdf

vue
<template>
  <button @click="exportPDF">导出pdf</button>
</template>

<script lang="ts" setup>
import jsPDF from "jspdf"
function exportPDF() {
  const doc = new jsPDF()
  doc.text("Hello world!", 10, 10)
  let u8Arr = new Uint8Array(doc.output("arraybuffer"))
  const blob = new Blob([u8Arr], {
    type: "application/pdf"
  })
  let fileURL = URL.createObjectURL(blob)
  window.open(fileURL)
}
</script>
<template>
  <button @click="exportPDF">导出pdf</button>
</template>

<script lang="ts" setup>
import jsPDF from "jspdf"
function exportPDF() {
  const doc = new jsPDF()
  doc.text("Hello world!", 10, 10)
  let u8Arr = new Uint8Array(doc.output("arraybuffer"))
  const blob = new Blob([u8Arr], {
    type: "application/pdf"
  })
  let fileURL = URL.createObjectURL(blob)
  window.open(fileURL)
}
</script>

node中使用jspdf导出pdf

使用nodejs特有视图Buffer读写arraybuffer

js
const { jsPDF } = require("jspdf") 
const fs = require("fs")
const doc = new jsPDF()
doc.text("Hello world!", 10, 10)
let rawdata = doc.output("arraybuffer")
let buffer = new Buffer(rawdata)
fs.writeFile("1.pdf", buffer, function (e) {
  if (e) throw e
  console.log("文件已被保存")
})
const { jsPDF } = require("jspdf") 
const fs = require("fs")
const doc = new jsPDF()
doc.text("Hello world!", 10, 10)
let rawdata = doc.output("arraybuffer")
let buffer = new Buffer(rawdata)
fs.writeFile("1.pdf", buffer, function (e) {
  if (e) throw e
  console.log("文件已被保存")
})

使用TypedArray(Uint8Array)读写arraybuffer

js
const { jsPDF } = require("jspdf")
const fs = require("fs")
const doc = new jsPDF()
doc.text("Hello world!", 10, 10)
let rawdata = doc.output("arraybuffer")
fs.writeFile("1.pdf", new Uint8Array(rawdata), function (e) {
  if (e) throw e
  console.log("文件已被保存")
})
const { jsPDF } = require("jspdf")
const fs = require("fs")
const doc = new jsPDF()
doc.text("Hello world!", 10, 10)
let rawdata = doc.output("arraybuffer")
fs.writeFile("1.pdf", new Uint8Array(rawdata), function (e) {
  if (e) throw e
  console.log("文件已被保存")
})

粘贴图片

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <style>
      body,
      html {
        text-align: center;
      }
      #editDiv {
        width: 1000px;
        display: inline-block;
        height: 800px;
        background: #fff;
        border-radius: 10px;
        line-height: 20px;
        padding: 10px;
        font-size: 14px;
        color: #666;
        resize: none;
        outline: none;
        overflow-y: scroll;
      }
      #editDiv {
        border: 1px solid #333;
        border-color: rgba(169, 169, 169, 1);
        text-align: left;
      }
    </style>
  </head>
  <body>
    <div id="editDiv" contenteditable="true"></div>
  </body>
  <script src="js/jquery.min.js"></script>
  <script>
    document.querySelector("#editDiv").addEventListener(
      "paste",
      function (e) {
        var cbd = e.clipboardData
        var ua = window.navigator.userAgent
        // 如果是 Safari 直接 return
        if (!(e.clipboardData && e.clipboardData.items)) {
          return
        }
        // Mac平台下Chrome49版本以下 复制Finder中的文件的Bug Hack掉
        if (
          cbd.items &&
          cbd.items.length === 2 &&
          cbd.items[0].kind === "string" &&
          cbd.items[1].kind === "file" &&
          cbd.types &&
          cbd.types.length === 2 &&
          cbd.types[0] === "text/plain" &&
          cbd.types[1] === "Files" &&
          ua.match(/Macintosh/i) &&
          Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49
        ) {
          return
        }
        for (var i = 0; i < cbd.items.length; i++) {
          var item = cbd.items[i]
          if (item.kind == "file") {
            var blob = item.getAsFile()
            if (blob.size === 0) {
              return
            }
            /*-----------------------与后台进行交互 end-----------------------*/
            /*-----------------------不与后台进行交互 直接预览start-----------------------*/
            var reader = new FileReader()
            var imgs = new Image()
            imgs.file = blob
            reader.onload = (function (aImg) {
              return function (e) {
                aImg.src = e.target.result
              }
            })(imgs)
            reader.readAsDataURL(blob)
            document.querySelector("#editDiv").appendChild(imgs)
            /*-----------------------不与后台进行交互 直接预览end-----------------------*/
          }
        }
      },
      false
    )
  </script>
</html>

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
    <style>
      body,
      html {
        text-align: center;
      }
      #editDiv {
        width: 1000px;
        display: inline-block;
        height: 800px;
        background: #fff;
        border-radius: 10px;
        line-height: 20px;
        padding: 10px;
        font-size: 14px;
        color: #666;
        resize: none;
        outline: none;
        overflow-y: scroll;
      }
      #editDiv {
        border: 1px solid #333;
        border-color: rgba(169, 169, 169, 1);
        text-align: left;
      }
    </style>
  </head>
  <body>
    <div id="editDiv" contenteditable="true"></div>
  </body>
  <script src="js/jquery.min.js"></script>
  <script>
    document.querySelector("#editDiv").addEventListener(
      "paste",
      function (e) {
        var cbd = e.clipboardData
        var ua = window.navigator.userAgent
        // 如果是 Safari 直接 return
        if (!(e.clipboardData && e.clipboardData.items)) {
          return
        }
        // Mac平台下Chrome49版本以下 复制Finder中的文件的Bug Hack掉
        if (
          cbd.items &&
          cbd.items.length === 2 &&
          cbd.items[0].kind === "string" &&
          cbd.items[1].kind === "file" &&
          cbd.types &&
          cbd.types.length === 2 &&
          cbd.types[0] === "text/plain" &&
          cbd.types[1] === "Files" &&
          ua.match(/Macintosh/i) &&
          Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49
        ) {
          return
        }
        for (var i = 0; i < cbd.items.length; i++) {
          var item = cbd.items[i]
          if (item.kind == "file") {
            var blob = item.getAsFile()
            if (blob.size === 0) {
              return
            }
            /*-----------------------与后台进行交互 end-----------------------*/
            /*-----------------------不与后台进行交互 直接预览start-----------------------*/
            var reader = new FileReader()
            var imgs = new Image()
            imgs.file = blob
            reader.onload = (function (aImg) {
              return function (e) {
                aImg.src = e.target.result
              }
            })(imgs)
            reader.readAsDataURL(blob)
            document.querySelector("#editDiv").appendChild(imgs)
            /*-----------------------不与后台进行交互 直接预览end-----------------------*/
          }
        }
      },
      false
    )
  </script>
</html>

Released under the MIT License.