发送到 BinaryWrite() 的超过 4MB 的数据是否应该分块?

Should data exceeding 4MB sent to BinaryWrite() ever be chunked?

我有一些旧代码可以将二进制数据写入 Response 对象的 BinaryWrite() 方法(经典 ASP)。它以 4MB 块的形式将数据发送到 BinaryWrite(),但现在我想知道这是否有效,BinaryWrite() 是否设计用于处理串行数据块(或者它是否应该最多每次调用一次页面请求)。

我发现这个 link 描述了应该如何增加“响应缓冲限制”,增加它似乎已经解决了我看到的问题(根本没有使用我的分块代码)。 https://docs.microsoft.com/en-us/troubleshoot/iis/http-500-response-binarywrite

这是有问题的旧代码:

HRESULT STDMETHODCALLTYPE CQVMActiveHost::WriteData (const VOID* pcvData, DWORD cbData, __out DWORD* pcbWritten)
{
    HRESULT hr;
    DISPPARAMS dispParams = {0};
    VARIANT vWrite = {0}, vResult = {0};

    Check(LoadResponseObject());

    dispParams.cArgs = 1;
    dispParams.rgvarg = &vWrite;

    if(m_fSupportsBinary)
    {
        SAFEARRAYBOUND Bound;
        DWORD cbRemaining = cbData;

        Bound.lLbound = 0;
        vWrite.vt = VT_ARRAY | VT_UI1;

        while(0 < cbRemaining)
        {
            PVOID pbPtr;

            Bound.cElements = min(cbRemaining, 4 * 1024 * 1024);
            vWrite.parray = SafeArrayCreate(VT_UI1, 1, &Bound);
            CheckAlloc(vWrite.parray);

            SafeArrayAccessData(vWrite.parray, &pbPtr);
            CopyMemory(pbPtr, pcvData, Bound.cElements);
            SafeArrayUnaccessData(vWrite.parray);

            VariantClear(&vResult);
            Check(m_pResponse->Invoke(m_dispidBinaryWrite, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dispParams, &vResult, NULL, NULL));
            SafeArrayDestroy(vWrite.parray);
            vWrite.parray = NULL;

            pcvData = reinterpret_cast<const BYTE*>(pcvData) + Bound.cElements;
            cbRemaining -= Bound.cElements;
        }

        vWrite.vt = VT_EMPTY;
    }
    else

我在旧代码中看到了一些不同的行为。在某些测试中,对 BinaryWrite() 的第一次调用成功,但后续调用失败并显示“发生异常”HRESULT。在其他测试中,似乎调用成功,但浏览器没有收到任何数据。

是否存在使用分块数据多次调用 BinaryWrite() 有意义的场景?

或者我是否应该始终将“响应缓冲限制”值增加到 4MB 以上,然后使用完整数据对 BinaryWrite() 进行一次调用?

谢谢!

我想知道我最初写上面代码时Response.Buffer 属性是不是false。我发现的内容如下:

  1. 每个页面请求可以多次调用 Response.BinaryWrite() 方法。
  2. 如果要返回给客户端的数据量很大,那么把数据拆分成多个调用给Response.BinaryWrite()
  3. IIS 中的“响应缓冲限制”值(ASP)默认为 4MB。
  4. 如果 Response.Buffertrue,则可能会多次调用 Response.BinaryWrite(),直到总数据达到“响应缓冲限制”值。此时,必须调用 Response.Flush()。否则,尝试发送更多数据会导致错误 0x80020009。
  5. 如果 Response.Bufferfalse,则不要调用 Response.Flush(),而是将数据分成多个(较小的)调用 Response.BinaryWrite()
  6. 例如,我尝试使用多次调用 Response.BinaryWrite() 发送一个 12MB 的文件,每个块为 4MB。启用了缓冲,因此第一次调用成功,但下一次调用失败。将“响应缓冲限制”提高到 16MB“解决”了这个问题,但增加了 ASP.
  7. 的缓冲分配

最终,我修改了分块代码以首先查询 Response.Buffer 属性。数据总是以较小的片段发送到 Response.BinaryWrite(),但如果启用缓冲,也会调用 Response.Flush()

最后,不要设置Content-Length header。浏览器可能不知道将下载多少字节,但它会正确接收文件,而无需手动设置 header。设置 header 中断下载。

最后的 ASP 脚本:

    function GoDownloadFile (strPath)
    {
        var idxName = strrchr(strPath, '/');
        var strFolder = left(strPath, idxName + 1);
        var strFile = right(strPath, len(strPath) - (idxName + 1));

        var oFS = Security.GetChannel().OpenFileSystem();
        oFS.Folder = strFolder;

        Response.ContentType = Host.GetContentType(strFile);
        Response.AddHeader("Content-Disposition", "attachment; FileName=\"" + strFile + "\"");

        Host.BinaryWrite(oFS.ReadFile(strFile));
    }