【问题标题】:Is their any alternative of HSE_REQ_TRANSMIT_FILE for large file offset?对于大文件偏移,他们是否可以替代 HSE_REQ_TRANSMIT_FILE?
【发布时间】:2018-01-27 06:38:42
【问题描述】:

我对 HSE_REQ_TRANSMIT_FILE 所需的 HSE_TF_INFO 结构成员偏移量有疑问。

让我解释一下我的问题:

我有一个 ISAPI 扩展(32 位),可以将图像发送回 Web 客户端(JPEG 小于 100 KB)。由于这些图像数量众多(超过 500 万张),我创建了一种文件格式,将所有图像存储(和索引)在一个文件中。当然这个文件大于 32 位偏移的 4GBytes 限制。

所以关于请求,我的 ISAPI 扩展在大文件中寻找合适的偏移量,读取小图像并将其发送回客户端。 我尝试通过异步发送这些小图像来改善我的 ISAPI 扩展的响应时间,因为它似乎更有效。

所以我尝试了 HSE_REQ_TRANSMIT_FILE,这正是我发送包装在大文件中的小图像所需要的:

  1. 一个文件句柄(在大文件上);
  2. (小图像的)偏移量;
  3. 以及(我的小图像的)大小。

但是 HSE_TF_INFO::Offset 是一个 DWORD,所以限制为 4 Go !!!!!! 所以奇怪的是,ReadFile 函数(从句柄读取)作为一个 OVERLAPED(似乎是为异步 I/O 设计的结构)作为两个偏移成员(Offset 和 OffsetHigh)来模拟 64 位偏移,因此支持大文件搜索。

有没有一种可以支持大文件偏移的HSE_TF_INFO_EX?或者是否有一种特殊的方法可以使用 HSE_TF_INFO 来查找超过 4GB 的文件?

感谢您的帮助...

【问题讨论】:

  • 是的,不幸的是,在 7 年内,forums.iis.net 上没有答案:(可能是因为您不在此论坛中;)

标签: winapi iis iis-7.5 iis-8 isapi


【解决方案1】:

您可以使用HSE_REQ_VECTOR_SEND - 这比HSE_REQ_TRANSMIT_FILE 更通用。如何查看HSE_REQ_VECTOR_SEND 让发送任意数量的HSE_VECTOR_ELEMENT 结构。我们如何查看HSE_VECTOR_ELEMENT 让发送文件或内存缓冲区。如果文件 - cbOffset(文件中的偏移量)和 cbSize(来自 cbOffset 的文件数据的字节数) - 两者都是 ULONGLONG - 所以没有大小限制

演示(纯粹是为了清晰)我们如何将HSE_REQ_VECTOR_SEND 转换为更通用的HSE_REQ_VECTOR_SEND

BOOL Repack(LPEXTENSION_CONTROL_BLOCK lpECB, LPHSE_TF_INFO ti)
{
    // code have same effect as call 
    // lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_TRANSMIT_FILE, ti, 0, 0);

    if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_IO_COMPLETION, ti->pfnHseIO, 0, (DWORD*)ti->pContext))
    {
        HSE_VECTOR_ELEMENT Element[3] = {
            { HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER, ti->pHead, 0, ti->HeadLength },
            { HSE_VECTOR_ELEMENT_TYPE_FILE_HANDLE, ti->hFile, ti->Offset, ti->BytesToWrite },
            { HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER, ti->pTail, 0, ti->TailLength }
        };

        HSE_RESPONSE_VECTOR rv = {
            HSE_IO_ASYNC|HSE_IO_DISCONNECT_AFTER_SEND|HSE_IO_FINAL_SEND,
            0,
            0,
            RTL_NUMBER_OF(Element),
            Element
        };

        if (!Element[2].cbSize) rv.nElementCount--;

        if (!Element[0].cbSize) rv.lpElementArray++, rv.nElementCount--;

        if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_VECTOR_SEND, &rv, 0, 0))
        {
            return TRUE;
        }
    }

    return FALSE;
}

因此,如果您需要完整的 64 位大小和偏移量,您通常需要下一个代码:

struct TRANSMIT_FILE_CONTEXT 
{
    ULONGLONG _BytesToWrite;
    ULONGLONG _cbOffset;
    HANDLE _hFile;
    char _szHeaders[];

    TRANSMIT_FILE_CONTEXT(HANDLE hFile, ULONGLONG BytesToWrite, ULONGLONG _cbOffset);

    ~TRANSMIT_FILE_CONTEXT();

    void* operator new(size_t cb, ULONG cbHeaders)
    {
        return ::operator new(cb + cbHeaders);
    }

    void operator delete(void* p)
    {
        ::operator delete(p);
    }

    static VOID WINAPI OnIoComplete (
        LPEXTENSION_CONTROL_BLOCK lpECB,
        PVOID pContext,
        DWORD /*cbIO*/,
        DWORD dwError
        )
    {
        DbgPrint("OnIoComplete(%p, %u)\n", lpECB->ConnID, dwError);

        dwError = (dwError == NOERROR ? HSE_STATUS_SUCCESS : HSE_STATUS_ERROR);

        lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_DONE_WITH_SESSION, &dwError, 0, 0);

        delete reinterpret_cast<TRANSMIT_FILE_CONTEXT*>(pContext);
    }

    DWORD Send(LPEXTENSION_CONTROL_BLOCK lpECB)
    {
        if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_IO_COMPLETION, OnIoComplete, 0, (DWORD*)this))
        {
            HSE_VECTOR_ELEMENT Element[2] = {
                { HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER, _szHeaders, 0, strlen(_szHeaders) },
                { HSE_VECTOR_ELEMENT_TYPE_FILE_HANDLE, _hFile, _cbOffset, _BytesToWrite }
            };

            HSE_RESPONSE_VECTOR rv = {
                HSE_IO_ASYNC|HSE_IO_DISCONNECT_AFTER_SEND|HSE_IO_FINAL_SEND,
                0,
                0,
                RTL_NUMBER_OF(Element),
                Element
            };

            if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_VECTOR_SEND, &rv, 0, 0))
            {
                return HSE_STATUS_PENDING;
            }
        }

        delete this;

        return HSE_STATUS_ERROR;
    }
};

DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
{
  //...
                if (TRANSMIT_FILE_CONTEXT* p = new(cb + 1) TRANSMIT_FILE_CONTEXT(*))
                {
                    //...

                    return p->Send(lpECB);
                }
    //...

    return HSE_STATUS_ERROR;
}

还要注意HSE_REQ_VECTOR_SENDHSE_REQ_TRANSMIT_FILE 内部调用HttpSendResponseEntityBody

【讨论】:

  • @RbMn 我很高兴你在这里,我没有想到 HSE_REQ_VECTOR_SEND。我让问题再开放几天,让我有时间实施并验证它
  • @lok​​i - HSE_REQ_TRANSMIT_FILEHSE_REQ_VECTOR_SEND 的特例。我昨天在自我 ISAPI 扩展中检查了这个 - 完美的工作。首先,我认为这两个请求映射到TransmitFileTransmitPackets,但是当调试代码时——查看HSE_REQ_ 上的两个请求实际上调用了相同的HttpSendResponseEntityBody。在HTTP_DATA_CHUNK我们的数据中。对于文件块 - 在HTTP_BYTE_RANGE](msdn.microsoft.com/en-us/library/windows/desktop/…) 结构中 - 其中StartingOffsetLengthULARGE_INTEGER
  • 我相信你在这里遗漏了一些重要的东西——清理传入的 hFile 句柄。当你使用标志中指定的 HSE_IO_ASYNC 调用向量发送时,你基本上需要交出所有权TRANSMIT_FILE_CONTEXT 结构的 HANDLE - 使其负责在 OnIOComplete 或结构的析构函数上关闭句柄 - 否则您将泄漏文件句柄。
  • @CWoods - 关闭文件句柄在 ~TRANSMIT_FILE_CONTEXT(); - 操作完成时调用。但是我的代码 sn-p 中没有显示析构函数的代码
  • 如果Send 失败(说是因为HSE_REQ_VECTOR_SEND 失败)-我只是调用delete this(所以调用了析构函数)并在此处关闭文件并清理其他资源并返回HSE_STATUS_ERROR。否则我返回HSE_STATUS_PENDING。当Send 完成时-OnIoComplete 被调用-在这里我完成了初始操作HSE_REQ_DONE_WITH_SESSION,最后删除自我-delete reinterpret_cast&lt;TRANSMIT_FILE_CONTEXT*&gt;(pContext);-调用析构函数,其中关闭文件句柄。所以析构函数总是调用一次 - 或者只是,如果开始发送失败,或者当它完成时
猜你喜欢
  • 2016-01-28
  • 1970-01-01
  • 1970-01-01
  • 2020-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-01
  • 2021-12-19
相关资源
最近更新 更多