首页 > 解决方案 > 是否应该对发送到 BinaryWrite() 的超过 4MB 的数据进行分块?

问题描述

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

我发现这个链接描述了应该如何增加“响应缓冲限制”,并且增加它似乎已经解决了我看到的问题(根本不使用我的分块代码)。 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() 进行一次调用?

谢谢!

标签: winapiiisvisual-c++asp-classic

解决方案


我不得不怀疑该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. 例如,我试图通过多次调用来发送一个 12MB 的文件Response.BinaryWrite(),每个块为 4MB。缓冲已启用,因此第一次调用成功,但下一次调用失败。将“响应缓冲限制”提高到 16MB“解决了”问题,但增加了 ASP 的缓冲分配。

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

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

最后的 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));
    }

推荐阅读