You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用openpgpjs库流式解密大文件时内存占用过高问题求助

问题分析与解决方案

问题根源

  1. 全量内存缓存解密数据:你的webStreamToBlob函数通过循环读取流的所有chunk并存储在数组中,最终一次性转换为Blob。对于大文件,这会导致整个文件内容加载到内存中,直接引发内存占用过高。
  2. 多余的Blob转换:fileToStream函数将File对象先转为Blob再获取stream,而File本身就是Blob的子类,直接调用file.stream()即可,多余的转换会增加不必要的内存开销。
  3. 无效配置与冗余状态存储:解密时配置preferredCompressionAlgorithm是无效的(该配置仅用于加密);同时将解密后的完整File对象存储到React状态中,进一步加剧内存压力。

优化后的代码实现

以下是修复内存问题的完整代码:

import React, { useState } from "react";
import * as openpgp from "openpgp";

const FileEncrypter = () => {
    const [file, setFile] = useState(null);
    const [password, setPassword] = useState("");
    const [encryptedFile, setEncryptedFile] = useState(null);

    const handleFileChange = (e) => {
        const selectedFile = e.target.files[0];
        setFile(selectedFile);
    };

    const handlePasswordChange = (e) => {
        setPassword(e.target.value);
    };

    // 直接使用File的原生stream方法,无需转Blob
    const fileToStream = (file) => {
        return file.stream();
    }

    const streamEncryptFile = async (fileToEncrypt) => {
        const fileName = `${file.name}.pgp`;
        const readableStream = fileToStream(fileToEncrypt);
        const message = await openpgp.createMessage({ binary: readableStream });
        
        const encryptedStream = await openpgp.encrypt({
            message,
            passwords: [password],
            format: 'binary',
            config: { preferredCompressionAlgorithm: openpgp.enums.compression.zlib }
        });

        // 用浏览器原生Response高效转换流到Blob
        const blob = await new Response(encryptedStream).blob();
        console.log("Encrypt Output Size: ", formatBytes(blob.size));
        return new File([blob], fileName, { type: 'application/octet-stream' });
    }

    // 重构解密逻辑:直接流式处理,避免缓存全量数据
    const handleDecryptClick = async () => {
        if (!encryptedFile || !password) return;

        const readableStream = fileToStream(encryptedFile);
        const message = await openpgp.readMessage({ binaryMessage: readableStream });
        
        const decrypted = await openpgp.decrypt({
            message,
            passwords: [password],
            format: 'binary'
        });

        // 直接基于解密流创建下载,无需存储完整文件到内存/状态
        const blob = await new Response(decrypted.data).blob();
        const downloadUrl = URL.createObjectURL(blob);
        
        const downloadLink = document.createElement('a');
        downloadLink.href = downloadUrl;
        downloadLink.download = file.name;
        downloadLink.click();
        
        // 及时释放URL对象,避免内存泄漏
        URL.revokeObjectURL(downloadUrl);
    };

    const formatBytes = (bytes, decimals = 2) => {
        if (!+bytes) return '0 Bytes'
        const k = 1024
        const dm = decimals < 0 ? 0 : decimals
        const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
        const i = Math.floor(Math.log(bytes) / Math.log(k))
        return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`
    }

    const handleEncryptClick = async () => {
        if (!file || !password) return;
        const encrypted = await streamEncryptFile(file);
        setEncryptedFile(encrypted);
    };

    return (
        <div>
            <input type="file" onChange={handleFileChange} />
            <input type="text" placeholder="Password" onChange={handlePasswordChange} />
            <button onClick={handleEncryptClick}>Encrypt</button>
            {encryptedFile && <button onClick={handleDecryptClick}>Decrypt & Download</button>}
            <br />
            {encryptedFile && (
                <a href={URL.createObjectURL(encryptedFile)} download={`${file.name}.pgp`}>
                    Download encrypted file
                </a>
            )}
        </div>
    );
};

export default FileEncrypter;

关键优化点说明

  1. 简化流转换:fileToStream直接返回file.stream(),去掉多余的Blob转换,减少内存开销。
  2. 高效流转Blob:使用new Response(stream).blob()替代手动读取所有chunk,浏览器内部会更高效地处理流数据,减少内存碎片化。
  3. 避免全量内存缓存:解密后直接创建下载链接,不将完整文件存储到React状态或内存数组中,而是让浏览器流式处理下载过程。
  4. 及时释放资源:调用URL.revokeObjectURL()释放下载链接占用的内存,避免内存泄漏。
  5. 移除无效配置:解密时去掉preferredCompressionAlgorithm配置,该选项仅对加密过程生效。

内容的提问来源于stack exchange,提问作者Sumit Sahoo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 13:45:20