Blob、File、ArrayBuffer、Data URL、Object URL、FileReader、URL使用

Blob对象和File对象

Blob(Binary Large Object)表示二进制类型的大对象。Blob 类型的对象表示不可变的类似文件对象的原始数据。

File 接口基于 Blob,继承了Blob 功能并将其扩展为支持用户系统上的文件。
 let blobParts = ["<html><h2>Hello welcome</h2></html>"];
let blob = new Blob(blobParts, { type: "text/html", endings: "transparent" });
console.log("size: " + blob.size + " bytes  ;", "type: " + blob.type); //size: 35 bytes  ; type: text/html

blobParts = [JSON.stringify({ name: "aa" })];
blob = new Blob(blobParts, { type: "application/json", endings: "transparent" });
console.log("size: " + blob.size + " bytes  ;", "type: " + blob.type); //size: 13 bytes  ; type: application/json

let hello = new Uint8Array([72, 101, 108, 108, 111]); // 二进制格式的 "hello"
blob = new Blob([hello, "world"], { type: "text/plain" });

const file = new File(["a".repeat(1000000)], "test.txt");
document.querySelector("#file5").addEventListener("change", (e) => {
    const file = e.target.files[0];
});

ArrayBuffer 与 TypedArray

 ArrayBuffer 对象用来表示「通用的、固定长度的」原始二进制数据缓冲区。ArrayBuffer 不能直接操作,而是要通过类型数组对象 或 DataView 对象来操作,该对象以特定格式表示缓冲区,并使用该对象读取和写入缓冲区的内容。
ArrayBuffer 本身只是一行 0 和 1 串。ArrayBuffer 不知道该数组中第一个元素和第二个元素之间的分隔位置。
let arrayBuffer = new ArrayBuffer(8);
let uint8 = new Uint8Array(arrayBuffer);
console.log(uint8.length, uint8[0]); //8 0

const fileReader = new FileReader();
fileReader.readAsArrayBuffer(blob);
fileReader.onload = function (res) {
    const arrayBuffer = res.target.result;
    const uint8 = new Uint8Array(arrayBuffer);
    console.log(uint8.length, uint8[0]); //10 72
};

Data URL和Object URL

Data URL 由四个部分组成:前缀(data:)、指示数据类型的 MIME 类型、如果非文本则为可选的 base64 标记、数据本身
在 Web 项目开发过程中,为了减少 HTTP 请求的数量,对应一些较小的图标,我们通常会考虑使用 Data URL 的形式内嵌到 HTML 或 CSS 文件中。
但需要注意的是:如果图片较大,图片的色彩层次比较丰富,则不适合使用这种方式,因为该图片经过 base64 编码后的字符串非常大,会明显增大 HTML 页面的大小,从而影响加载速度。」

Object URL 是一种伪协议,也被称为 Blob URL。它允许 Blob 或 File 对象用作图像,下载二进制数据链接等的 URL 源。
在浏览器中,我们使用 URL.createObjectURL 方法来创建 Blob URL,该方法接收一个 Blob 对象,并为其创建一个唯一的 URL。
浏览器内部为每个通过 URL.createObjectURL 生成的 URL 存储了一个 「URL → Blob」 映射。因此,此类 URL 较短,但可以访问 Blob。生成的 URL 仅在当前文档打开的状态下才有效。
如果我们创建一个 Blob URL,即使不再需要该 Blob,它也会存在内存中。针对这个问题,我们可以调用 URL.revokeObjectURL(url) 方法,从内部映射中删除引用,从而允许删除 Blob(如果没有其他引用),并释放内存。
const objectURL = URL.createObjectURL(blob);
console.log(objectURL); //blob:http://localhost:3000/fdd9687a-f914-4e4c-841d-f48966d2db60

FileReader

readAsDataURL 图片预览

<fieldset style="margin-top: 2rem">
    <legend>图片预览:</legend>
    <input type="file" id="file" accept="image/*" />
    <img src="" id="img" style="width: 50px" />
</fieldset>
<script>
      document.querySelector("#file").addEventListener("change", (e) => {
        const file = e.target.files[0];
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = (res) => {
          document.querySelector("#img").src = res.target.result;
        };
      });
</script>

readAsText 读取文本文件

<fieldset style="margin-top: 2rem">
      <legend>读取文本文件( txt,csv):</legend>
      <input type="file" id="file1" />
      <p id="text-progress"></p>
      <p id="text" style="min-height: 100px; height: auto; border: 1px solid silver"></p>
</fieldset>
<script>
     document.querySelector("#file1").addEventListener("change", (e) => {
        const file = e.target.files[0];
        const reader = new FileReader();
        reader.readAsText(file);
        reader.onloadstart = (res) => {
          if (res.total < 1) {
            reader.abort();
          }
        };
        reader.onload = (res) => {
          document.querySelector("#text").innerHTML = res.target.result;
        };
        reader.onprogress = (res) => {
          const timer = setInterval(function () {
            const progress = (res.loaded / res.total).toFixed(2);
            if (progress == "1.00") clearInterval(timer);
            document.querySelector("#text-progress").innerHTML = progress * 100 + "%";
          }, 100);
        };
        reader.onabort = (res) => {
          console.log("aborted");
        };
        reader.onloadend = (res) => {
          console.log("loaded");
        };
      });
</script>

readAsArrayBuffer

<fieldset style="margin-top: 2rem">
      <legend>检测图片真实类型:</legend>
      <input type="file" id="file2" />
      <p>图片类型为:<span id="text-type1"></span></p>
      <p>MIME类型为:<span id="text-type2"></span></p>
</fieldset>
<script>
    document.querySelector("#file2").addEventListener("change", (e) => {
        const file = e.target.files[0];
        const reader = new FileReader();
        reader.onload = (res) => {
          const uint8Array = new Uint8Array(res.target.result);
          document.querySelector("#text-type1").innerText = check(uint8Array);
          document.querySelector("#text-type2").innerText = file.type;
        };
        reader.readAsArrayBuffer(file.slice(0, 8));
      });

      function check(buffers) {
        const headers = {
          JPEG: [0xff, 0xd8, 0xff],
          PNG: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
          GIF: [0x47, 0x49, 0x46, 0x38],
          BMP: [0x42, 0x4d],
        };
        for (let type in headers) {
          if (headers[type].every((header, index) => header === buffers[index])) {
            return type;
          }
        }
        return "";
      }     
</script>
     

图片处理

<fieldset style="margin-top: 2rem">
      <legend>图像灰度化、压缩:</legend>
      <img id="preview1" style="height: 50px" />
      <canvas id="canvas" width="80" height="50"></canvas>
      <img id="preview2" style="height: 50px" />
      <img id="compress" style="height: 50px" />
</fieldset>
<script>
      const image1 = document.querySelector("#preview1");
      const image2 = document.querySelector("#preview2");
      fetch("https://www.baidu.com/img/flexible/logo/pc/result.png")
        .then((response) => response.blob())
        .then((blob) => {
          image1.src = URL.createObjectURL(blob);
          image1.onload = () => {
            draw(image1);
            compress();
          };
        });
      fetch("https://www.baidu.com/img/flexible/logo/pc/result.png")
        .then((response) => response.arrayBuffer())
        .then((buffer) => {
          const blob = new Blob([buffer]);
          const objectURL = URL.createObjectURL(blob);
          image2.src = objectURL;
        });

      function draw(image) {
        const canvas = document.querySelector("#canvas");
        const ctx = canvas.getContext("2d");
        ctx.drawImage(image, 0, 0, 80, 50);
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imageData.data;

        const grayscale = function () {
          for (let i = 0; i < data.length; i += 4) {
            const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
            data[i] = avg; // red
            data[i + 1] = avg; // green
            data[i + 2] = avg; // blue
          }
          ctx.putImageData(imageData, 0, 0);
        };
        grayscale();
      }

      function compress(quality = 80, mimeType = "image/webp") {
        const compressImage = document.querySelector("#compress");
        const canvas = document.querySelector("#canvas");
        const imageDataURL = canvas.toDataURL(mimeType, quality / 100);
        compressImage.src = imageDataURL;
      }
</script>
 

 大文件分片上传

<fieldset style="margin-top: 2rem">
      <legend>大文件分片上传:</legend>
      <input type="file" id="file5" />
      <p id="text5" style="height: 50px"></p>
</fieldset>
<script>
      //大文件分片上传
      document.querySelector("#file5").addEventListener("change", (e) => {
        // const file = new File(["a".repeat(1000000)], "test.txt");
        const file = e.target.files[0];

        const chunkSize = 400000;
        const url = "http://localhost/test.php";

        async function chunkedUpload() {
          const totalChunk = Math.ceil(file.size / chunkSize);
          let i = 0;
          const files = [];
          for (let start = 0; start < file.size; start += chunkSize) {
            i++;
            const chunk = file.slice(start, start + chunkSize);
            const fd = new FormData();
            fd.append("part", chunk);
            fd.append("filename", file.name);
            fd.append("total", totalChunk);
            fd.append("index", i);
            files.push(await fetch(url, { method: "post", body: fd }).then((res) => res.text()));
          }

          await fetch(url, {
            method: "post",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({ files: files, filename: file.name }),
          })
            .then((res) => res.text())
            .then((res) => {
              document.querySelector("#text5").innerHTML = res;
            });
        }
        chunkedUpload();
      });
</script>
后端php:
<?php
if (isset($_FILES['part'])) {
    //formdata格式
    $to = './uploads/' . time() . random_int(1000, 9999) . '.part';
    move_uploaded_file($_FILES['part']['tmp_name'], $to);
    echo json_encode(['index' => $_REQUEST['index'], 'total' => $_REQUEST['total'], 'file' => $to]);
    die;
} else {
    //json格式
    $data = json_decode(file_get_contents('php://input'), true);
    $filename = $data['filename'];
    $tmp = explode('.', $filename);
    $type = $tmp[count($tmp) - 1];

    $res = [];
    foreach ($data['files'] as $item) {
        $res[] = json_decode($item, 1);
    }
    usort($res, function ($itemA, $itemB) {
        return $itemA['index'] - $itemB['index'];
    });

    $outFile = "./test." . $type;
    $out = fopen($outFile, 'a');
    foreach ($res as $item) {
        $part = fopen($item['file'], 'rb');
        flock($part, LOCK_EX);
        $contents = fread($part, filesize($item['file']));
        flock($part, LOCK_UN);
        fwrite($out, $contents);
        @unlink($item['file']);
    }
    fclose($out);
    echo json_encode(['code' => 0, 'msg' => 'success']);
    die;
}

URL

<div>
  <input type="file" id="file" accept="image/*" />
  <br />
  <img id="img" src="" alt="" width="100px" />
</div>

<script>
  let urlstring = "this is not a URL";
  let url;

  try {
    url = new URL(urlstring);
  } catch {
    console.log("not a url");
  }

  url = URL.canParse(urlstring) && new URL(urlstring);
  console.log(url); //false

  urlstring = "http://localhost:3000/html-demos/test-url.html?a=2&b=0#aa";
  url = URL.canParse(urlstring) && new URL(urlstring);
  console.log(url); //URL对象
  console.log("href", url.href); //http://localhost:3000/html-demos/test-url.html?a=2&b=0#aa
  console.log("protocol", url.protocol); //http:
  console.log("host", url.host); //localhost:3000
  console.log("hostname", url.hostname); //localhost
  console.log("port", url.port); //3000
  console.log("pathname", url.pathname); ///html-demos/test-url.html
  console.log("search", url.search); //?a=2&b=0
  console.log("hash", url.hash); //#aa

  // url.searchParams 是URLSearchParams对象,有append、delete、set、get、getAll、has、entries、keys、values、forEach、sort等方法
  for (let [key, value] of url.searchParams) {
    console.log(key, value);
  }

  urlstring = "ftp://user:pass@192.168.1.120:60021/path/to/file.jpg";
  url = URL.canParse(urlstring) && new URL(urlstring);
  console.log(url);
  console.log("protocol", url.protocol); //http:
  console.log("username", url.username); //user
  console.log("password", url.password); //pass

  urlstring = "https://www.baidu.com/admin";
  url = new URL(urlstring);
  url = new URL("login", url);
  console.log(url); //https://www.baidu.com/login

  document.querySelector("#file").onchange = function () {
    const file = this.files[0];
    const elem = document.querySelector("#img");
    const url = URL.createObjectURL(file);
    elem.src = url;

    // URL.revokeObjectURL(url);
  };
</script>

posted @   carol2014  阅读(58)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示