#c #windows #http #download #wininet
#c #Windows #http #Скачать #wininet
Вопрос:
У меня возникла проблема с моими HTTP-запросами в моем приложении, так что, если удаленный файл имеет тот же размер, что и локальный файл (даже если время его изменения отличается, поскольку его содержимое было изменено), попытки загрузить его возвращаются быстро, и более новый файл не загружается.
Короче говоря, процесс, которому я следую, заключается в следующем: Настраиваю HTTP-соединение с INTERNET_FLAG_RESYNCHRONIZE
флагом и вызываю HttpSendRequest(); затем проверяю код состояния HTTP и нахожу, что он равен «200».
- Если удаленный файл обновлен, но остается того же размера, что и локальная копия: локальный файл не изменился после запуска приложения. Если я вызываю
HttpQueryInfo()
withHTTP_QUERY_LAST_MODIFIED
после отправки запроса, он выдает мне фактическое время последнего изменения файла сервера, которое, как я вижу, отличается от локального файла, который я пытаюсь перезаписать. - Если удаленный файл обновляется, и размер файла становится отличным от локальной копии: он загружается и перезаписывает локальную копию, как и ожидалось.
Вот довольно сокращенная версия кода, позволяющая исключить помощников и проверку ошибок:
// szAppName = our app name
HINTERNET hInternetHandle = InternetOpen( szAppName,
INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );
// szServerName = our server name
hInternetHandle = InternetConnect( hInternetHandle, szServerName,
INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, 0 );
// szPath = the file to download
LPCSTR aszDefault[2] = { "*/*", NULL };
DWORD dwFlags = 0
| INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP
| INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS
| INTERNET_FLAG_KEEP_CONNECTION
| INTERNET_FLAG_NO_AUTH
| INTERNET_FLAG_NO_AUTO_REDIRECT
| INTERNET_FLAG_NO_COOKIES
| INTERNET_FLAG_NO_UI
| INTERNET_FLAG_RESYNCHRONIZE;
HINTERNET hHandle = HttpOpenRequest( hInternetHandle, "GET", szPath, NULL,
NULL, aszDefault, dwFlags, 0 );
DWORD dwTimeOut = 10 * 1000; // In milliseconds
InternetSetOption( hInternetHandle, INTERNET_OPTION_CONNECT_TIMEOUT,
amp;dwTimeOut, sizeof( dwTimeOut ) );
InternetSetOption( hInternetHandle, INTERNET_OPTION_RECEIVE_TIMEOUT,
amp;dwTimeOut, sizeof( dwTimeOut ) );
InternetSetOption( hInternetHandle, INTERNET_OPTION_SEND_TIMEOUT,
amp;dwTimeOut, sizeof( dwTimeOut ) );
DWORD dwRetries = 5;
InternetSetOption( hInternetHandle, INTERNET_OPTION_CONNECT_RETRIES,
amp;dwRetries, sizeof( dwRetries ) );
HttpSendRequest( hInternetHandle, NULL, 0, NULL, 0 );
Поскольку я обнаружил, что могу запросить время последнего изменения удаленного файла и найти его точным, я знаю, что он действительно попадает на сервер. Я думал, что указание INTERNET_FLAG_RESYNCHRONIZE
принудит файл к повторной синхронизации, если он устарел. У меня все неправильно? Именно так это и должно работать?
Редактировать: я провел некоторое расследование с помощью анализатора пакетов, и вот некоторая дополнительная информация:
Если удаленный и локальный файлы в точности совпадают, это обмен:
GET /test.bmp HTTP/1.1
Accept: */*
If-None-Match: "1c1467112ee6ca1:369"
User-Agent: Internal Testing
Host: ****************
Connection: Keep-Alive
HTTP/1.1 304 Not Modified
Last-Modified: Tue, 27 Apr 2010 17:21:26 GMT
Accept-Ranges: bytes
ETag: "1c1467112ee6ca1:369"
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Date: Tue, 27 Apr 2010 18:10:26 GMT
Теперь, если удаленный файл изменился, но размер файла остался прежним:
GET /test.bmp HTTP/1.1
Accept: */*
If-None-Match: "1c1467112ee6ca1:369"
User-Agent: Internal Testing
Host: ****************
Connection: Keep-Alive
HTTP/1.1 200 OK
Content-Length: 419958
Content-Type: image/bmp
Last-Modified: Tue, 27 Apr 2010 18:11:17 GMT
Accept-Ranges: bytes
ETag: "b65425835e6ca1:369"
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Date: Tue, 27 Apr 2010 18:11:33 GMT
[Block of data]
Итак, сервер действительно отправляет файл, когда он изменился, но мое приложение по-прежнему считает, что он не изменился. Я думаю, проблема заключается в том, как мое приложение обрабатывает ответ; это не мой собственный код, и парень, который его написал, давно ушел.
Одна из проблем, которую я обнаружил, заключается в том, что в обоих приведенных выше сценариях при вызове HttpQueryInfo()
с HTTP_QUERY_STATUS_CODE
я получаю обратно 200. Однако в первом приведенном выше случае я вижу, что фактический ответ сервера был 304, а не 200. Покопавшись в используемом нами коде, я обнаружил, что он, похоже, пытается обойти это, сравнивая размер файла и предполагая, что файл не изменился, если размеры файлов одинаковы; отсюда и проблема, с которой я столкнулся!
Итак, теперь мой вопрос проще: почему HttpQueryInfo()
возвращает 200, даже если сервер возвращает одну из ошибок 3XX? Я обнаружил, что некоторые люди задают аналогичный вопрос онлайн, но они либо не получили ответов, либо имели дело напрямую с веб-браузерами.
Ответ №1:
Я полагаю, что это INTERNET_FLAG_RESYNCHRONIZE
использует If-Modified-Since
поле заголовка HTTP-запроса и проверяет код состояния, в то время как HTTP_QUERY_LAST_MODIFIED будет (я думаю) просто выполнять запрос HEAD и проверяет поле заголовка HTTP-ответа Last-Modified
.
Попробуйте HTTP_QUERY_IF_UNMODIFIED_SINCE
amp; a HTTP_QUERY_LAST_MODIFIED
с HttpQueryInfo()
и сравните результаты [в качестве дополнительного примечания, если у вас запущен HTTP-сниффер, это может прояснить некоторые вещи для всех нас …].
В качестве быстрого и грязного решения вы можете использовать INTERNET_FLAG_RELOAD
вместо INTERNET_FLAG_RESYNCHRONIZE
, чтобы принудительно перезагружать файл каждый раз, когда вы его запрашиваете.
HTH
Комментарии:
1. Я уже проверял HTTP_QUERY_LAST_MODIFIED, который выдавал мне правильное время изменения удаленного файла. Я только что проверил HTTP_QUERY_IF_UNMODIFIED_SINCE, который возвращает мне пустую строку. Я только что опробовал анализатор HTTP-пакетов, и он утверждает, что запрос на .jpg вернул 1138 байт содержимого: размер файла (как удаленного, так и локального размера, которые одинаковы). Что-нибудь еще, что я должен искать с помощью сниффера?
2. Было бы неплохо, если бы вы опубликовали фактические HTTP-заголовки (запрос и ответ) при запуске вашей программы. Кроме того, есть возможность попробовать флаг INTERNET_FLAG_RELOAD для HttpOpenRequest?
3. Хорошо, я опубликовал дополнительную информацию в вопросе вместе с некоторыми обработанными запросами / ответами. Поскольку сервер фактически отправляет файл, когда он изменился, переключение на использование _RELOAD ничего не меняет с точки зрения приложения.
Ответ №2:
Согласно MSDN INTERNET_FLAG_RESYNCHRONIZE
, только «перезагружает HTTP-ресурсы, если ресурс был изменен с момента его последней загрузки». Смотрите http://msdn.microsoft.com/en-us/library/aa383661 (v = против 85).aspx.
200, возвращенный HttpQueryInfo()
, действительно странный — он должен возвращать реальный код состояния.
Если вы можете, измените код, чтобы сравнивать контрольную сумму вместо сравнения размера файла.
Если нет, вы можете посмотреть на еще несколько вещей:
- Отключите ETags на сервере — смотрите: http://developer.yahoo.com/performance/rules.html#etags и http://support.microsoft.com/?id=922733
- Добавьте небольшой дополнительный параметр для вашего изображения, который гарантирует, что оно
всегда загружается в виде случайного числа или контрольной суммы файла:
http://yourserver/image.jpg?r=5452435234
- Отключите кэширование на вашем клиенте с помощью
INTERNET_FLAG_NO_CACHE_WRITE
и
INTERNET_FLAG_PRAGMA_NOCACHE
в
HttpOpenRequest()
иInternetOpenUrl()
как описано в
http://msdn.microsoft.com/en-us/library/aa383661 (VS.85).aspx#INTERNET_FLAG_NO_CACHE_WRITE.