使用 openpgpjs 库进行流式解密对大文件占用过多内存

问题描述 投票:0回答:0

我正在使用 openpgp 库来加密和解密 React 组件内的文件。加密按预期工作,内存使用量较少,但解密占用过多内存。 我使用的库版本是:5.7.0

下面是带有加解密功能的代码示例。加密类型与密码对称。

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 [decryptedFile, setDecryptedFile] = useState(null);

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

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

    const fileToStream = (file) => {
        const blob = new Blob([file], { type: file.type });
        const stream = blob.stream();
        return stream;
    }


    const streamEncryptFile = async (fileToEncrypt) => {

        const fileName = `${file.name}.pgp`;

        const readableStream = fileToStream(fileToEncrypt);
        const message = await openpgp.createMessage({ binary: readableStream });
        const encrypted = await openpgp.encrypt({
            message, // input as Message object
            passwords: [password], // multiple passwords possible
            format: 'binary', // don't ASCII armor (for Uint8Array output)
            config: { preferredCompressionAlgorithm: openpgp.enums.compression.zlib } // compress the data with zlib
        });

       
        const blob = await webStreamToBlob(encrypted);
        console.log("Encrypt Output Size: ", formatBytes(blob.size));

        return new File([blob], fileName, { type: 'application/octet-stream' });

    }

    const streamDecryptFile = async (fileToDecrypt) => {

        const fileName = `${file.name}`;

        const readableStream = fileToStream(fileToDecrypt);
        const message = await openpgp.readMessage({ binaryMessage: readableStream });
        const decrypted = await openpgp.decrypt({
            message, // input as Message object
            passwords: [password], // multiple passwords possible
            format: 'binary', // don't ASCII armor (for Uint8Array output)
            config: { preferredCompressionAlgorithm: openpgp.enums.compression.zlib } // compress the data with zlib

        });

        const blob = await webStreamToBlob(decrypted.data);
        console.log("Decrypt Output Size: ", formatBytes(blob.size));

        return new File([blob], fileName, { type: 'application/octet-stream' });

    }

    const webStreamToBlob = async (webStream) => {
        try {
            const reader = webStream.getReader();
            //const reader = webStream.getReader({ chunkSize: 1 * 1024 * 1024 });
            const chunks = [];
            let done, value;
            while (!done) {
                ({ done, value } = await reader.read());
                if (value) {
                    console.log("Chunk Count: ", chunks.length + 1);
                    chunks.push(value);
                }
            }
            const blob = new Blob(chunks, { type: 'application/octet-stream' });
            return blob;
        } catch (error) {
            console.error('Error in coverting to blob:', error);
            //throw new Error('Failed to convert WebStream to Blob.');
        }
    }

    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 () => {

        const encryptedFile = await streamEncryptFile(file);
        setEncryptedFile(encryptedFile);

    };

    const handleDecryptClick = async () => {

        const decryptedFile = await streamDecryptFile(encryptedFile);
        setDecryptedFile(decryptedFile);

    };

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

export default FileEncrypter;

javascript reactjs encryption-symmetric openpgp openpgp.js
© www.soinside.com 2019 - 2024. All rights reserved.