Форум программистов
 
Регистрация на форуме тут, о проблемах пишите сюда - alarforum@yandex.ru, проверяйте папку спам! Обязательно пройдите активизацию e-mail, а тут можно восстановить пароль.

Как купить рекламу на форуме


Вернуться   Форум программистов > Низкоуровневое программирование > Win Api
Регистрация

Восстановить пароль
Повторная активизация e-mail


Ответ
 
Опции темы
Старый 11.12.2020, 11:32   #1
BLACK_RAIN
Форумчанин
 
Регистрация: 13.02.2012
Сообщений: 867
Вопрос Скачать файл в многопотоке

Здравствуйте.
Для ускорения скачивания файла, его необходимо скачивать в многопотоке. Создаём несколько потоков, каждый из которых качает разные части файла. Потом их склеиваем и готово.
Но наткнулся на проблему. Всё работает только если качаемый файл уже находится в кеше. А если файла в кеше нет, то первый поток качает несколько килобайт или мегабайт и зависает. Остальные потоки запускаются и создают файлы, но ничего не качают.
А если вызывать InternetOpenUrl() так:
Код:
    hUrl := InternetOpenUrl(hInet, PChar(URL), nil, 0,
                                INTERNET_FLAG_NO_CACHE_WRITE or INTERNET_FLAG_RELOAD, 0);
или так
    hUrl := InternetOpenUrl(hInet, PChar(URL), nil, 0, INTERNET_FLAG_RELOAD, 0);
то вообще ничего не работает. Либо не срабатывает сама функция InternetOpenUrl, либо последующие функции возвращают не 200, либо что-то еще идёт по трубе.
Что делать?

Последний раз редактировалось BLACK_RAIN; 11.12.2020 в 11:34.
BLACK_RAIN вне форума Ответить с цитированием
Старый 11.12.2020, 16:25   #2
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,317
По умолчанию

Цитата:
Сообщение от BLACK_RAIN Посмотреть сообщение
Что делать?
Не использовать InternetOpenUrl.
Вместо этого, HttpOpenRequest, HttpSendRequest, InternetReadFile.
waleri на форуме Ответить с цитированием
Старый 11.12.2020, 18:36   #3
BLACK_RAIN
Форумчанин
 
Регистрация: 13.02.2012
Сообщений: 867
По умолчанию

Цитата:
Сообщение от waleri Посмотреть сообщение
Вместо этого, HttpOpenRequest, HttpSendRequest,
они же вроде только для POST / GET запросов. Как ими качать файлы?
BLACK_RAIN вне форума Ответить с цитированием
Старый 11.12.2020, 19:23   #4
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,317
По умолчанию

Цитата:
Сообщение от BLACK_RAIN Посмотреть сообщение
только для POST / GET запросо
А InternetOpenUrl какой запрос посылает?

Цитата:
Сообщение от BLACK_RAIN Посмотреть сообщение
Как ими качать файлы
GET запросами.
waleri на форуме Ответить с цитированием
Старый 11.12.2020, 20:11   #5
BLACK_RAIN
Форумчанин
 
Регистрация: 13.02.2012
Сообщений: 867
По умолчанию

Цитата:
Сообщение от waleri Посмотреть сообщение
GET запросами.
то есть, вот так
Код:
function Https_Get(sURL : string; var RecvText : AnsiString) : Integer;
const
  sUserAgent = 'Mozilla/5.001 (windows; U; NT4.0; en-US; rv:1.0) Gecko/25250101';
var
  hInet, hConnect, hRequest : HINTERNET;
  lpdwBufferLength: DWORD;
  lpdwReserved    : DWORD;
  dwBytesRead     : DWORD;
  lpdwNumberOfBytesAvailable : DWORD;
  b, _pos : Cardinal;
  ServerName, Resource, Header : string;
  ResponseText : AnsiString;
  p : PChar;
begin
  Result := -1;
  _pos := Pos('://', sURL);
  Header := Copy(sURL, 1, _pos + 2);
  Delete(sURL, 1, _pos + 2);
  _pos := Pos('/', sURL);
  ServerName := Copy(sURL, 1, _pos - 1);
  Delete(sURL, 1, _pos - 1);
  Resource := sURL;
  hInet := InternetOpen(PChar(sUserAgent), INTERNET_OPEN_TYPE_PRECONFIG,
                        nil, nil, 0);
  if Assigned(hInet) then
  begin
    hConnect := InternetConnect(hInet, PChar(ServerName),
                  INTERNET_DEFAULT_HTTPS_PORT, nil, nil,
                  INTERNET_SERVICE_HTTP, 0, 0);
    if Assigned(hConnect) then
    begin
      p := PChar('Accept: application/vnd.twitchtv.v5+json' + #0);
      hRequest := HttpOpenRequest(hConnect, 'GET', PChar(Resource),
                  HTTP_VERSION, '', @p, INTERNET_FLAG_SECURE, 0);
      if Assigned(hRequest) then
      begin
        Header := 'Client-ID: ' + TWITCH_CLIENT_ID;
        if not HttpSendRequest(hRequest, PChar(Header), Length(Header), nil, 0) then
        begin
          InternetCloseHandle(hRequest);
          InternetCloseHandle(hConnect);
          InternetCloseHandle(hInet);
          Exit;
        end;

        lpdwBufferLength := SizeOf(dword);
        lpdwReserved := 0;
        if not HttpQueryInfo(hRequest,
                             HTTP_QUERY_STATUS_CODE or HTTP_QUERY_FLAG_NUMBER,
                             @result, lpdwBufferLength, lpdwReserved) then
        begin
          InternetCloseHandle(hRequest);
          InternetCloseHandle(hConnect);
          InternetCloseHandle(hInet);
          Exit;
        end;

        if Result = 200 then
        begin
          _Pos := 1;
          ResponseText := '';
          repeat
            if not InternetQueryDataAvailable(hRequest,
                                lpdwNumberOfBytesAvailable, 0, 0) then
            begin
              InternetCloseHandle(hRequest);
              InternetCloseHandle(hConnect);
              InternetCloseHandle(hInet);
              Exit;
            end;
            SetLength(ResponseText, Length(ResponseText) +
                           lpdwNumberOfBytesAvailable);
            InternetReadFile(hRequest, @ResponseText[_pos],
                            lpdwNumberOfBytesAvailable, b);
            Inc(_Pos, b);
          until b = 0;
          RecvText := ResponseText;
        end;
        InternetCloseHandle(hRequest);
      end;
      InternetCloseHandle(hConnect);
    end;
    InternetCloseHandle(hInet);
  end;
end;
только без заголовков и в TStream?
BLACK_RAIN вне форума Ответить с цитированием
Старый 12.12.2020, 08:26   #6
BLACK_RAIN
Форумчанин
 
Регистрация: 13.02.2012
Сообщений: 867
По умолчанию

Код:
  n := Pos('://', URL);
  t := Copy(URL, 1, n + 2);
  Delete(fURL, 1, n + 2);
  n := Pos('/', URL);
  ServerName := Copy(URL, 1, n - 1);
  Delete(fURL, 1, n - 1);
  Resource := URL;

  hInet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG,
                        nil, nil, 0);
  if not Assigned(hInet) then
  begin
    Writeln('InternetOpen() failed');
    Exit;
  end;
  hConnect := InternetConnect(hInet, PChar(ServerName),
                  INTERNET_DEFAULT_HTTP_PORT, nil, nil,
                  INTERNET_SERVICE_HTTP, 0, 0);
  if not Assigned(hConnect) then
  begin
    Writeln('InternetConnect() failed');
    InternetCloseHandle(hInet);
    Exit;
  end;
  hRequest := HttpOpenRequest(hConnect, 'GET', PChar(Resource),
                      HTTP_VERSION, '', nil, 0, 0);
  if not Assigned(hRequest) then
  begin
    InternetCloseHandle(hConnect);
    InternetCloseHandle(hInet);
    Exit;
  end;
  if not HttpSendRequest(hRequest, nil, 0, nil, 0) then
  begin
    InternetCloseHandle(hRequest);
    InternetCloseHandle(hConnect);
    InternetCloseHandle(hInet);
    Exit;
  end;
  dwIndex := 0;
  dwBufferLen := 256;
  ZeroMemory(@perrorcode, 256);
  if not HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE or HTTP_QUERY_FLAG_NUMBER,
                     @pErrorCode, dwBufferLen, dwIndex) then
  begin
    InternetCloseHandle(hRequest);
    InternetCloseHandle(hConnect);
    InternetCloseHandle(hInet);
    Exit;
  end;
Почему HttpQueryInfo возвращает букву Ё? Что это значит?

Последний раз редактировалось BLACK_RAIN; 12.12.2020 в 09:26.
BLACK_RAIN вне форума Ответить с цитированием
Старый 12.12.2020, 10:41   #7
BLACK_RAIN
Форумчанин
 
Регистрация: 13.02.2012
Сообщений: 867
По умолчанию

Если убрать HttpQueryInfo , то работает. Но качает только первый поток. Остальные не качают.
Цитата:
Сообщение от waleri Посмотреть сообщение
GET запросами.
как именно?
BLACK_RAIN вне форума Ответить с цитированием
Старый 12.12.2020, 11:34   #8
BLACK_RAIN
Форумчанин
 
Регистрация: 13.02.2012
Сообщений: 867
По умолчанию

Решил посмотреть, что будет если подождать завершения скачивания. Выяснилась странная хрень.
Первый поток начинает качать сразу. Второй только через какое-то время (примерно через две минуты, но это не точно). Третьего и остальных я не стал дожидаться.
Код выглядит так:
Код:
procedure TThreadDownload.Execute;
var
  d : TWinApiDownload;
  e : Integer;
begin
  d := TWinApiDownload.Create;
  d.URL := Url;
  d.StartPos := StartPos;
  d.EndPos := EndPos;
  d.fFilename := FileName;
  if FileExists(FileName) then
  DeleteFile(FileName);
  Stream := TFileStream.Create(FileName, fmCreate or fmOpenReadWrite);
  e := d.Download(Stream);
  d.Free;
end;

procedure TMultyThreadDownload.Download;
var
  i : Integer;
  thr : TThreadDownload;
  FileSize, ChunkSize : Integer;
begin
  FileSize := GetFileSize;
  ChunkSize := Round(FileSize / ThreadCount);
  for I := 0 to ThreadCount - 1 do
  begin
    thr := TThreadDownload.Create(True);
    thr.Priority := tpHigher;
    thr.FreeOnTerminate := False;
    thr.OnTerminate := EventThreadComplete;
    thr.FileName := OutputFileName + '_' + IntToStr(i);
    thr.Url := Url;
    thr.StartPos := ChunkSize * i;
    if i = ThreadCount - 1 then
    thr.EndPos := 0 else
    thr.EndPos := (ChunkSize * i) + ChunkSize;
    thr.Completed := False;

    fThreads[i] := thr;
    thr.Start;
  end;

end;

function TMultyThreadDownload.GetFileSize;
var
  d : TWinApiDownload;
begin
  d := TWinApiDownload.Create;
  Result := d.GetFileSize(Url);
  d.Free;
end;

procedure TMultyThreadDownload.SetThreadsCount(n: Integer);
begin
  ThreadCount := n;
  SetLength(fThreads, n);
end;
Объясните, почему остальные потоки такие слоупочные и кого они ждут?

А в NetLimier'е написано, что в моём процессе есть 9 потоков и 8 из них что-то качают. Получается, что они качают один файл? Но ведь в коде не это написано.
Что вообще происходит?

Последний раз редактировалось BLACK_RAIN; 12.12.2020 в 11:58.
BLACK_RAIN вне форума Ответить с цитированием
Старый 12.12.2020, 12:13   #9
BLACK_RAIN
Форумчанин
 
Регистрация: 13.02.2012
Сообщений: 867
По умолчанию

Я понял. Проблема в функции InternetSetFilePointer(). Если ее убрать, то все потоки стартуют одновременно и начинают качать сразу. Но тогда все потоки качают файл с самого начала.
Как указать, с какого места нужно скачивать файл?
BLACK_RAIN вне форума Ответить с цитированием
Старый 12.12.2020, 12:57   #10
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 6,317
По умолчанию

Цитата:
Сообщение от BLACK_RAIN Посмотреть сообщение
Как указать, с какого места нужно скачивать файл?
https://tools.ietf.org/html/rfc2616
waleri на форуме Ответить с цитированием
Ответ


Купить рекламу на форуме 20000 рублей в месяц

Опции темы


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
скачать файл brownb Общие вопросы Delphi 5 12.08.2017 12:07
Скачать файл KaDima28 JavaScript, Ajax 1 29.12.2014 07:41
curl: скачать файл WebbMan C/C++ Сетевое программирование 2 23.04.2011 14:21
Скачать файл... Romix Работа с сетью в Delphi 1 28.07.2010 08:37
Скачать файл из архива pesi Работа с сетью в Delphi 4 29.09.2009 20:28


Проекты отопления, пеллетные котлы, бойлеры, радиаторы
интернет магазин respective.ru
Пеллетный котёл Emtas
котлы EMTAS