Dela via


Ladda upp filer till en IIS WebDav-katalog med hjälp av WinINet-API:et

Den här artikeln beskriver hur du programmatiskt laddar upp filer till en WebDav-katalog som finns på Microsoft Internet Information Services (IIS) med hjälp av WinINet-API:et.

Ursprunglig produktversion: Internet Information Services
Ursprungligt KB-nummer: 2001156

Symptom

Programmet laddar programmatiskt upp filer till en WebDav-katalog som finns på IIS, med hjälp av WinINet-API:et för att skicka ett HTTP-verb (Hypertext Transfer Protocol). PUT Du märker att programmet fungerar korrekt på IIS och Windows Server, men kan misslyckas med att ladda upp filer till IIS WebDav-katalogen, även om inga kodändringar har gjorts i WinINet-programmet.

Orsak

Orsaken till det här problemet beror på en designändring för WebDav som körs på IIS. WebDav som körs på IIS kräver nu autentisering och fungerar inte om endast anonym autentisering används. Därför kommer ditt program som använder WinINet API-sekvensen för HttpSendRequestEx, InternetWriteFileHttpEndRequest att uppleva att anropet till HttpEndRequest returnerar FALSE och GetLastError() anger en felkod 12032 – ERROR_INTERNET_FORCE_RETRY.

Åtgärd

Lösningen på det här problemet är att försöka utföra samma åtgärdssekvens igen, nämligen:

  1. HttpSendRequestEx
  2. InternetWriteFile
  3. HttpEndRequest

Tills HttpEndRequest returnerar inte FALSE och GetLastError() rapporterar inte tillbaka 12032 (eller så finns det något annat fel). Om IIS visas med felaktig autentiseringsinformation fortsätter IIS att returnera http-fel 401 för varje nytt försök. Därför måste du hålla reda på hur många gånger HttpEndRequest funktionen returnerar fel 12032 och förhindra att den körs i en oändlig loop.

Om det finns Windows NTLM-autentisering HttpEndRequest returneras fel 12032 i högst två gånger för att uppfylla trevägshandskakningen i NTLM. Det första felet 12032 anger ett HTTP-fel 401-svar från servern och det andra felet 12032 anger typ 2 NTLM-handskakningsmeddelandet från servern, vilket, om giltig autentiseringsinformation skickas till IIS, användaren autentiseras korrekt och uppladdningen lyckas.

När du använder en omprövningslogik för att anropa funktionerna ovan i en loop ser du att anropet till InternetWriteFile görs flera gånger. Det innebär att anropet till slutar skriva data via nätverket, vilket leder till InternetWriteFile slöseri med bandbredd. För att förhindra detta kan du skicka en HTTP-dummy-begäran HEAD till servern, som i förväg autentiserar begäran och gör att det senare anropet av HttpSendRequest inte skickar HTTP-nyttolasten när InternetWriteFile anropas. Om du är bekant med nätverksövervakaren eller WinINet-loggning ser du att den första PUT begäran som skickas till servern har en innehållslängd på noll, vilket förhindrar nyttolastöverföringen och nyttolasten överförs inte förrän NTLM-handskakningen är klar.

WebDav-funktionen kräver inte att du använder Windows-autentisering. du kan konfigurera din WebDav-server att använda grundläggande autentisering via SSL, vilket säkerställer säkerheten för datauppladdningen. När grundläggande autentisering har konfigurerats kan du direkt mata in en giltig base-64-kodad lösenordssträng för användarnamn i den utgående begäran, vilket förhindrar att IIS returnerar ett HTTP-fel 401 och det är därför HttpEndRequest som inte returnerar fel 12032. Du kan lägga till grundläggande autentiseringsinformation i den utgående begäran genom att anropa WinINet-API:et:

HttpAddRequestHeaders(hRequest, "Authorization: Basic <<valid base-64 encoded username:password string>>\r\n", -1, HTTP_ADDREQ_FLAG_ADD);

Gör detta innan du anropar HttpSendRequestEx för att direkt mata in auktoriseringshuvudet i den utgående HTTP-begäran.

Följande kodexempel visar hur du kan använda logiken för återförsök för att hantera retursvaret 12032 från HttpEndRequest. Det här exemplet omfattar inte den förautentiserade begäran som beskrivs ovan. För att förautentisera HttpSendRequestEx behöver du bara anropa HttpOpenRequest, HttpSendRequest med HTTP-verbet HEAD till målservern innan du anropar koden nedan.

Kodexempel

BOOL UseHttpSendReqEx(HINTERNET hRequest, DWORD dwPostSize)
{
    INTERNET_BUFFERS BufferIn;
    DWORD dwBytesWritten;
    int iChunkCtr;
    BYTE pBuffer[1024];
    BOOL bRet;
    BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS ); // Must be set or you will get an error
    BufferIn.Next = NULL;
    BufferIn.lpcszHeader = NULL;
    BufferIn.dwHeadersLength = 0;
    BufferIn.dwHeadersTotal = 0;
    BufferIn.lpvBuffer = NULL;
    BufferIn.dwBufferLength = 0;
    BufferIn.dwBufferTotal = dwPostSize; // This is the only member used other than dwStructSize
    BufferIn.dwOffsetLow = 0;
    BufferIn.dwOffsetHigh = 0;
    //  The following variable will keep track of the number of times HttpSendRequestEx is called
    int iNumTrials = 0;
    bool bRetVal = FALSE;
    //  The retry goto is to re-try the operation when HttpEndRequest returns error 12032.
    while(1)
    {
        if(!HttpSendRequestEx( hRequest, &BufferIn, NULL, 0, 0))
        {
            printf( "Error on HttpSendRequestEx %d\n",GetLastError());
            return FALSE;
        }
        FillMemory(pBuffer, 1024, 'D'); // Fill buffer with data
        bRet=TRUE;
        for(iChunkCtr=1; iChunkCtr<=(int)dwPostSize/1024 && bRet; iChunkCtr++)
        {
            dwBytesWritten = 0;
            if(bRet=InternetWriteFile( hRequest, pBuffer, 1024, &dwBytesWritten))
                printf( "\r%d bytes sent.", iChunkCtr*1024);
        }
        if(!bRet)
        {
            printf( "\nError on InternetWriteFile %lu\n",GetLastError());
            return FALSE;
        }
        if(!HttpEndRequest(hRequest, NULL, 0, 0))
        {
            int iLastError = GetLastError();
            printf( "Error on HttpEndRequest %lu \n", iLastError);
            //  Use the following logic to "retry" after receiving error 12032 from HttpEndRequest
            //
            //  Error 12032 = ERROR_INTERNET_FORCE_RETRY means that you just need to send the request again
            //
            //  Sending request again means that you simply need to call:
            //
            //  HttpSendRequest, InternetWriteFile, HttpEndRequest until HttpEndRequest does not return
            //  back error 12032.
            //
            //  Since NTLM is a 3-way handshake protocol, it will happen that HttpEndRequest will return
            //  error 12032 two times and hence the following check.
            //
            //  If error 12032 is returned 3 or more number of times, then there is some Other error.
            if(iLastError == 12032 && iNumTrials < 3) {
                iNumTrials++;
                continue;   // This will retry HttpSendRequestEx...
            }
            return FALSE;
        }
        return TRUE;
    }
}

Mer information