Azure Blob Storage 分片上传方式(预签名 url)

azure 云存储方式有三种传输方式:block blobs, append blobs, and page blobs。这里我们只涉及到上传,所以 Block blob 方式足够。

用户上传的 file

首先拿到用户上传的文件,需要符合 file:File 类型,一般是 <input type="file"> 上传拿到的数据,这里的 File 支持 slice 方法可以切片

预签名接口 preSignedUploadUrl

前后端分离的项目,一般后端会存储上传的认证信息密钥之类,每次前端上传,请求后端接口,后端生成一个临时的预签名(pre-signed)url,一般几分钟之后会失效。前端使用这个预签名 url (后面提到的 preSignedUploadUrl) 进行上传操作。

整个文件一次性上传 uploadComplete

如果文件的size比较小,比如 file.size < 4 * 1024 * 1024 文件小于 4MB,那么可以选择一次上传全部。

ts 复制代码
async function uploadComplete(preSignedUploadUrl:string,file:File) {
  return axios.put(preSignedUploadUrl, file, {
    headers: {
      "x-ms-blob-type": "BlockBlob", // block blob 方式上传
    },
  });
}

分片上传 uploadChunks

分片操作在 azure 这里需要分两步:

  1. 一段一段上传,可以顺序,或乱序,但是浏览器一般会限制并发数(比如 chrome 限制为 6 个),按需考虑。
  2. 上传需要合并的分片信息 xml 结构的表,里面的 blockId 信息需要按照上传顺序依次排列,告诉 azure 需要合并这个请求

注意:分片上传和提交的合并分片表单两个接口,是同一个预签名 url。

这里的 demo 中使用了 await-to-js ,将整个上传流程更直观,同步化

ts 复制代码
import to from "await-to-js";
import { v4 as uuidv4 } from "uuid";

async function uploadChunks(preSignedUploadUrl:string, file:File) {
  const fileSize = file.size;
  const CHUNK_SIZE = 4 * 1024 * 1024; // 4 M
  const totalChunks = Math.ceil(fileSize / CHUNK_SIZE);
  const blockIdList = [];

  // 1. 传递每一个分片
  for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
    const start = chunkIndex * CHUNK_SIZE;
    const end = Math.min(start + CHUNK_SIZE, fileSize);
    const chunk = file.slice(start, end);

    // 生成唯一的 blockId, 所有的 blockId 长度必须一致,否则,后传的就 error
    const blockId = btoa(uuidv4());
    blockIdList.push(blockId);

    // 注意这里 url 需要拼参数  &comp=block&blockid=${blockId}
    const url = `${preSignedUploadUrl}&comp=block&blockid=${blockId}`;

    const [err, values] = await to(
      axios.put(url, file, {
        headers: {
          "x-ms-blob-type": "BlockBlob",
          "Content-Type": file.type,
          "Content-Range": `bytes=${start}-${end}/${fileSize}`,
        },
      }),
    );
    if (err) {
      // err todo
    }
  }

  // 2. 提交合并分片的请求
  const xmlBodyString = `<?xml version="1.0" encoding="UTF-8"?><BlockList>${blockIds
    .map(blockId => `<Latest>${blockId}</Latest>`)
    .join("")}</BlockList>`;

// 注意这里 url 需要拼参数 &comp=blocklist 
  const [err, res] = await to(
    axios.put(`${preSignedUploadUrl}&comp=blocklist`, xmlBodyString, {
      headers: {
        "Content-Type": "text/plain",
      },
    }),
  );
  if (err) {
    // err todo
  }

  // end
}

上传进度 onUploadProgress

对于整个一次性的上传,使用 axios 的可以使用 onUploadProgress 进行进度同步更新。

js 复制代码
axios.put(upload_url, file, {
    headers: { "x-ms-blob-type": "BlockBlob", ...headers },
    onUploadProgress(progressEvent) {
    const { loaded, total } = progressEvent;
    if (!total || !loaded) return;

    const percent = Math.floor((loaded * 100) / total);

    onUpdate(percent); // 0-100
    },
});

对于分片的文件,可以用分片的进度和各个分片上传的进度,两者结合来做判断,大家可以尝试下,这里不过多着墨。

参考

Uploading Large Files in Windows Azure Blob Storage Using Shared Access Signature, HTML, and JavaScript

How to upload large files in chunks using rest API, azure blob list storage from salesforce using LWC.

相关推荐
Mintopia1 小时前
Web 安全与反编译源码下的权限设计:构建前后端一体的信任防线
前端·安全·编译原理
林深现海1 小时前
Jetson Orin nano/nx刷机后无法打开chrome/firefox浏览器
前端·chrome·firefox
黄诂多2 小时前
APP原生与H5互调Bridge技术原理及基础使用
前端
前端市界2 小时前
用 React 手搓一个 3D 翻页书籍组件,呼吸海浪式翻页,交互体验带感!
前端·架构·github
文艺理科生2 小时前
Nginx 路径映射深度解析:从本地开发到生产交付的底层哲学
前端·后端·架构
千寻girling2 小时前
主管:”人家 Node 框架都用 Nest.js 了 , 你怎么还在用 Express ?“
前端·后端·面试
C澒2 小时前
Vue 项目渐进式迁移 React:组件库接入与跨框架协同技术方案
前端·vue.js·react.js·架构·系统架构
xiaoxue..2 小时前
合并两个升序链表 与 合并k个升序链表
java·javascript·数据结构·链表·面试
清山博客2 小时前
OpenCV 人脸识别和比对工具
前端·webpack·node.js
要加油哦~2 小时前
AI | 实践教程 - ScreenCoder | 多agents前端代码生成
前端·javascript·人工智能