如何将大文件存储到网络本地存储?

To *_* Be 3 html javascript caching local-storage typescript

我正在开发从在线服务器下载文件的 ionic 2 应用程序,我想将这些文件存储到本地存储(或我不知道的缓存)。

我知道我可以使用以下方法将数据存储到本地存储:

localStorage.setItem('key','value');
Run Code Online (Sandbox Code Playgroud)

但是如果是文件,特别是大文件,如何存储到本地存储呢?

注意:我正在使用 TypeScript。

Dai*_*Dai 29

供您在 2022 年考虑:Origin 私有文件系统

截至 2022 年初,IndexedDB 仍然是持久保存二进制数据(例如图像和视频、来自<input type="file">JSFileBlob对象的用户文件等)的最佳整体选择,因为它支持异步 IO 并且能够保存原始二进制数据,而无需丑陋的黑客攻击就像 URI 的 Base64 编码一样data:

然而,IndexedDB 可能是 HTML5 中设计最差的 API 接口之一( “触发事件后,你连接事件处理程序?太疯狂了! ”),浏览器供应商也同意:所以 Apple 的 Safari 和 Google 的 Chromium 现在都一样支持一个更简单的数据持久化 API,名为Origin Private File System (OPFS),该 API 最近于 2021 年推出。


  • OPFS 是较大的 WHATWG 文件系统 API的子集,也与现有的 WHATWG 存储 API(定义localStorageindexedDB等)相关。

  • 尽管 OPFS 的名称包含术语“文件系统”,但 OPFS不涉及对用户实际本地计算机文件系统的任何访问:将其更多地视为一个隔离的虚拟或“假装”文件系统,其范围仅限于单个网站(特别是,范围仅限于单一来源),因此不同网站/来源无法访问其他网站的 OPFS 存储。

  • OPFS 缺乏初始固定大小的存储配额,而localStoragesessionStorage两者都有 5MB 的初始限制

    • 然而,这并不是邀请用户在 OPFS 中的计算机上存储千兆字节的垃圾:浏览器施加自己的软限制,如果达到限制,对 OPFS 的写入操作将抛出异常,QuotaExceededError您应该准备好妥善处理。
  • 由于 OPFS 不涉及用户实际的本地文件系统,并且空间是按来源隔离的(URI 方案 + 主机名 + 端口),这意味着 OPFS 不会带来用户隐私或计算机安全的风险,因此浏览器不会中断您的脚本,向您的用户显示可能看起来很吓人的安全提示,用户必须点击才能明确授予或拒绝您的网站或页面使用 OPFS API 的能力。

    • 尽管浏览器将来仍然可以添加这样的提示(如果他们选择),当然,浏览器总是可以禁用 OPFS 并拒绝所有使用它的尝试,就像今天indexedDB一样localStorage,甚至假装在页面加载或会话之间保留数据(例如 Chrome 的隐身模式)。

使用OPFS:

Apple 在 WebKit 博客中有详细的使用示例

简单介绍一下:您使用 OPFS vianavigator.storage和 waitinggetDirectory()来获取FileSystemHandle代表网站(源)存储空间的根目录的对象,然后在根上使用 wait 方法来创建和操作文件和子目录。

例如,假设您有一个<canvas>元素,其中包含用户手绘的一些非常棒的原创艺术作品,并且希望将其作为 PNG blob 本地保存在浏览器的 OPFS 存储空间中,那么您可以执行以下操作:

async function doOpfsDemo() {

    // Open the "root" of the website's (origin's) private filesystem:
    let storageRoot = null;
    try {
        storageRoot = await navigator.storage.getDirectory();
    }
    catch( err ) {
        console.error( err );
        alert( "Couldn't open OPFS. See browser console.\n\n" + err );
        return;
    }

    // Get the <canvas> element from the page DOM:
    const canvasElem = document.getElementById( 'myCanvas' );

    // Save the image:
    await saveCanvasToPngInOriginPrivateFileSystem( storageRoot, canvasElem );

    // (Re-)load the image:
    await loadPngFromOriginPrivateFileSystemIntoCanvas( storageRoot, canvasElem );
}

async function saveCanvasToPngInOriginPrivateFileSystem( storageRoot, canvasElem ) {

    // Save the <canvas>'s image to a PNG file to an in-memory Blob object: (see https://stackoverflow.com/a/57942679/159145 ):
    const imagePngBlob = await new Promise(resolve => canvasElem.toBlob( resolve, 'image/png' ) );

    // Create an empty (zero-byte) file in a new subdirectory: "art/mywaifu.png":
    const newSubDir = await storageRoot.getDirectoryHandle( "art", { "create" : true });
    const newFile   = await newSubDir.getFileHandle( "mywaifu.png", { "create" : true });

    // Open the `mywaifu.png` file as a writable stream ( FileSystemWritableFileStream ):
    const wtr = await newFile.createWritable();
    try {
        // Then write the Blob object directly:
        await wtr.write( imagePngBlob );
    }
    finally {
        // And safely close the file stream writer:
        await wtr.close();
    }
}

async function loadPngFromOriginPrivateFileSystemIntoCanvas( storageRoot, canvasElem ) {
    
    const artSubDir = await storageRoot.getDirectoryHandle( "art" );
    const savedFile = await artSubDir.getFileHandle( "mywaifu.png" ); // Surprisingly there isn't a "fileExists()" function: instead you need to iterate over all files, which is odd... https://wicg.github.io/file-system-access/
    
    // Get the `savedFile` as a DOM `File` object (as opposed to a `FileSystemFileHandle` object):
    const pngFile = await savedFile.getFile();
    
    // Load it into an ImageBitmap object which can be painted directly onto the <canvas>. You don't need to use URL.createObjectURL and <img/> anymore. See https://developer.mozilla.org/en-US/docs/Web/API/createImageBitmap
    // But you *do* still need to `.close()` the ImageBitmap after it's painted otherwise you'll leak memory. Use a try/finally block for that.
    try {
        const loadedBitmap = await createImageBitmap( pngFile ); // `createImageBitmap()` is a global free-function, like `parseInt()`. Which is unusual as most modern JS APIs are designed to not pollute the global scope.
        try {
            const ctx = canvasElem.getContext('2d');
            ctx.clearRect( /*x:*/ 0, /*y:*/ 0, ctx.canvas.width, ctx.canvas.height ); // Clear the canvas before drawing the loaded image.
            ctx.drawImage( loadedBitmap, /*x:*/ 0, /*y:*/ 0 ); // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
        }
        finally {
            loadedBitmap.close(); // https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap/close
        }
    }
    catch( err ) {
        console.error( err );
        alert( "Couldn't load previously saved image into <canvas>. See browser console.\n\n" + err );
        return;
    }
}
Run Code Online (Sandbox Code Playgroud)
  • 请记住,OPFS 文件系统空间并不代表用户计算机上的底层文件系统(如果有)FileSystemFileHandle : OPFS 空间中的现有文件不一定存在于用户的真实文件系统中。

    • 事实上,它们更有可能以与 IndexedDB blob 相同的方式进行物理持久化:作为嵌套在其他数据存储文件中的一系列字节,例如基于 Chromium 的浏览器使用自己的名为 LevelDB 的数据库引擎,Safari似乎使用日志式 Sqlite DB作为其 IndexedDB 后备存储。您可以自己查看它们:在 Windows 上,Chrome/Chromium/Edge 将 IndexedDB 的 LevelDB 数据库文件存储在C:\Users\{you}\AppData\Local\Google\Chrome\User Data\Default\IndexedDB.
  • OPFS 也非常简单:虽然计算机的 NTFS、ZFS 或 APFS 文件系统将具有权限/ACL、所有权、低级(即块级)访问、快照和历史记录等功能,但 OPFS 缺乏所有这些功能。这些:OPFS 甚至没有文件锁定机制,这通常被认为是基本的原始 FS 操作。

    • 显然,这使 OPFS 的 API 表面更简单:比其他方式更容易实现和使用,更不用说缩小潜在的攻击面,但这确实意味着 OPFS 确实更像是一种“本地 AWS S3”或者 Azure Blob 存储,它最终仍然足以满足95% 的应用程序的需要……只是不要指望能够在 OPFS 上的 JavaScript 中运行高性能并发预写日志数据库服务器。

基于浏览器的 JavaScript 文件系统 API 的比较:

但不要将 OPFS 与其他文件系统和文件系统式 API 混淆:



Ale*_*ara 5

为了存储相对较大的数据,我建议使用IndexedDB

相对于本地存储的优势:

  • 更大的尺寸限制
  • 支持字符串以外的其他数据类型(原生类型数组!)
  • 异步


Pra*_*jan 1

浏览器对本地存储中可以存储的数据量有限制。它因浏览器而异。

如果您的文件大小低于限制,您可以将文件内容复制为字符串并将其保存为“文件名”:“文件内容”的键值对