winapi - 是否应该对发送到 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() 进行一次调用?
谢谢!
解决方案
我不得不怀疑该Response.Buffer
属性是否是false
我最初编写上面代码的时候。我发现如下:
Response.BinaryWrite()
每个页面请求可以多次调用该方法。- 如果要向客户端返回大量数据,则将数据拆分为多次调用
Response.BinaryWrite()
. - IIS(对于 ASP)中的“响应缓冲限制”值默认为 4MB。
- 如果
Response.Buffer
是true
,则可以进行多次调用,Response.BinaryWrite()
直到总数据达到“响应缓冲限制”值。此时,Response.Flush()
必须调用。否则,尝试发送更多数据会导致错误 0x80020009。 - 如果
Response.Buffer
是false
,则不要调用Response.Flush()
,而是将数据拆分为多个(较小的)调用Response.BinaryWrite()
。 - 例如,我试图通过多次调用来发送一个 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));
}
推荐阅读
- plone - 如何从 Plone 的 ZMI 脚本中确定文件大小?
- java - 填充所有状态值的汇总计数
- java - 并行二维数组和数组的Java问题
- git - 如何将版本信息添加到作为 ZIP 存档分发的 git 存储库?
- javascript - 为什么 jQuery 选择器在这里不起作用?
- c++ - 分配给 gslice_array 会产生运行时错误
- flutter - 你如何在flutter中通过云功能从firestore获取documentSnapshot?
- javascript - jQuery 函数在 Promise.then() 中不可用
- laravel - laravel中Cache和Redis门面的区别?
- javascript - 在 ES6 承诺中的 for 循环中返回为“未定义”的值