gpt4 book ai didi

javascript - Chrome FileReader 为大文件返回空字符串 (>= 300MB)

转载 作者:行者123 更新时间:2023-12-04 03:33:15 24 4
gpt4 key购买 nike

目标:

  • 在浏览器中,从用户文件系统中读取一个文件作为 base64 字符串
  • 这些文件最大为 1.5GB

  • 问题:
  • 以下脚本在 Firefox 上运行良好。不管文件大小。
  • 在 Chrome 上,该脚本适用于较小的文件(我测试了大约 5MB 大小的文件)
  • 如果您选择更大的文件(例如 400MB),则 FileReader 将在没有错误或异常的情况下完成,但返回一个空字符串而不是 base64 字符串

  • 问题:
  • 这是 Chrome 错误吗?
  • 为什么既没有错误也没有异常?
  • 如何修复或解决此问题?

  • 重要提示:
    请注意,分 block 不是一个选项对我来说,因为我需要通过“POST”将完整的 base64 字符串发送到不支持 block 的 API。
    代码:

    'use strict';

    var filePickerElement = document.getElementById('filepicker');

    filePickerElement.onchange = (event) => {
    const selectedFile = event.target.files[0];
    console.log('selectedFile', selectedFile);

    readFile(selectedFile);
    };

    function readFile(selectedFile) {
    console.log('START READING FILE');
    const reader = new FileReader();

    reader.onload = (e) => {
    const fileBase64 = reader.result.toString();

    console.log('ONLOAD','base64', fileBase64);

    if (fileBase64 === '') {
    alert('Result string is EMPTY :(');
    } else {
    alert('It worked as expected :)');
    }
    };

    reader.onprogress = (e) => {
    console.log('Progress', ~~((e.loaded / e.total) * 100 ), '%');
    };

    reader.onerror = (err) => {
    console.error('Error reading the file.', err);
    };

    reader.readAsDataURL(selectedFile);
    }
    <!doctype html>
    <html lang="en">

    <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">

    <title>FileReader issue example</title>
    </head>

    <body>

    <div class="container">
    <h1>FileReader issue example</h1>
    <div class="card">
    <div class="card-header">
    Select File:
    </div>
    <div class="card-body">
    <input type="file" id="filepicker" />
    </div>
    </div>

    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"
    integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8"
    crossorigin="anonymous"></script>
    <script src="main.js"></script>
    </body>

    </html>

    最佳答案

    Is this a chrome bug?


    正如我在回答 Chrome, FileReader API, event.target.result === "" 时所说的那样,这是 V8(Chrome 的但也是 node-js 和其他的 JavaScript JS 引擎)的限制。
    这是故意的,因此不能真正称为“错误”。
    技术细节是,这里实际上失败的是在 64 位系统上构建超过 512MB(减去 header )的字符串,因为在 V8 中,所有堆对象都必须适合 Smi(小整数),(参见 this commit)。

    Why is there neither an error nor an exception?


    那可能是一个错误......正如我在链接答案中所展示的那样,直接创建这样的字符串时我们会得到一个 RangeError :

    const header = 24;
    const bytes = new Uint8Array( (512 * 1024 * 1024) - header );
    let txt = new TextDecoder().decode( bytes );
    console.log( txt.length ); // 536870888
    txt += "f"; // RangeError

    FileReader::readOperation 的第 3 步中, UA 必须

    If package data threw an exception error:

    • Set fr’s error to error.
    • Fire a progress event called error at fr.

    但是在这里,我们没有那个错误。

    const bytes = Uint32Array.from( { length: 600 * 1024 * 1024 / 4 }, (_) => Math.random() * 0xFFFFFFFF );
    const blob = new Blob( [ bytes ] );
    const fr = new FileReader();
    fr.onerror = console.error;
    fr.onload = (evt) => console.log( "success", fr.result.length, fr.error );
    fr.readAsDataURL( blob );

    我将打开一个关于此的问题,因为您应该能够从 FileReader 处理该错误。

    How can I fix or work around this issue?


    最好的绝对是让您的 API 端点直接接受二进制资源而不是 data://URL,无论如何都应该避免这种情况。
    如果这不可行,那么“ future ”的解决方案是将 POST 一个 ReadableStream 到您的端点,并在来自 Blob 的流上自己进行 data://URL 转换。
    class base64StreamEncoder {
    constructor( header ) {
    if( header ) {
    this.header = new TextEncoder().encode( header );
    }
    this.tail = [];
    }
    transform( chunk, controller ) {
    const encoded = this.encode( chunk );
    if( this.header ) {
    controller.enqueue( this.header );
    this.header = null;
    }
    controller.enqueue( encoded );
    }
    encode( bytes ) {
    let binary = Array.from( this.tail )
    .reduce( (bin, byte) => bin + String.fromCharCode( byte ), "" );
    const tail_length = bytes.length % 3;
    const last_index = bytes.length - tail_length;
    this.tail = bytes.subarray( last_index );
    for( let i = 0; i<last_index; i++ ) {
    binary += String.fromCharCode( bytes[ i ] );
    }
    const b64String = window.btoa( binary );
    return new TextEncoder().encode( b64String );
    }
    flush( controller ) {
    // force the encoding of the tail
    controller.enqueue( this.encode( new Uint8Array() ) );
    }
    }
    实时示例: https://base64streamencoder.glitch.me/
    目前,您必须将 base64 表示的 block 存储在 Blob 中,如 Endless 的回答所示。
    但是请注意,由于这是 V8 的限制,即使是服务器端也可能会遇到这么大的字符串问题,所以无论如何,您应该联系 API 的维护人员。

    关于javascript - Chrome FileReader 为大文件返回空字符串 (>= 300MB),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67483360/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com