Несмотря на все ловушки и проверки ошибки ввода вывода при чтении текста веб-станиц и картинок к ней из интернета всеравно могут сильно затормозить работу программы.
Идеи обхода проблемы:
1 Сравнительно быстрая проверка существования конкретного файла для конкретного URL перед основным чтением.
Результат :
Частично помогает избежать совсем уж тяжелых сбоев парсинга (например когда сеть по невнятным причинам выкатывает какую-нибудь "страницу-заглушку" ) но в то же время дополнительный тормоз даже при нормальном чтении .
2 Попытка читать данные "по байтам" то есть с контролем состояния при чтении каждого байта из потока
Результат :
Идея неплоха, но из за буферизации данных ОС нормально не работает .
3 Запускать каждую операцию чтения в отдельном потоке с самостоятельным отлеживанием таймаута.
Примечание:
Для ускорения загрузки галереи картинок это это приходится делать в лбом случае ( последовательное чтение их списка просто "вымораживает" программу ) но там это происходит чуть иначе.
Результат :
Заметное улучшение контролируемости процесса но в то же время возникновение проблемы "современности " данных ( "зависший" процесс может внезапно "откопаться из сугроба" и начать запись данных по давно "убитому" адресу в памяти ) и просто современного завершения "повисшего процесса".
(Попытки остановить и завершить процесс принудительно
Типа такого
- Код: Выделить всё
If (now-CTime)*10e4 >=OutTime then begin
LoadPicThread.Suspend;
While not LoadPicThread.Suspended do Application.ProcessMessages;;
LoadPicThread.Free;
Exit;
end;
приводит к тяжелым сбоям)
Конкретные вопросы:
1 Есть ли надежные и быстрые способы проверки сосуществования файла или страницы без их чтения?
Мои идеи поэтому поводу довольно ущербны:
- Код: Выделить всё
uses fphttpclient;
...
Var
ResponseCode:string;
Function TestURL_https(URL:String;Timeout,Attempts:Integer):Boolean;
Const httpclient: TFPHTTPClient=Nil;
var
RC,AT: integer;
S:string;
T:Byte;
Label L1;
begin
Result:=True;
At:=1;
httpclient := TFPHttpClient.Create(nil);
httpclient.IOTimeout:=Timeout;
try
SetProxy(httpclient,FSetup.ProxyEd.Text);
S:= httpclient.Options(Url);
except
Result:=False;
end;
RC:= httpclient.ResponseStatusCode;
ResponseStatusText := httpclient.ResponseStatusText ;
If httpclient.ResponseStatusText <>'OK' Then begin
ResponseStatusText :='Нет доступа';
Result:=False;
end;
httpclient.Free;
end;
if not Result and (at< Attempts ) then begin
AT:=AT+1; Goto L1;
end;
end;
и
с компонентом Инди (Indy10)
- Код: Выделить всё
uses
.. IdHTTP, IdSSLOpenSS,;
...
var
ResponseCode:String;
Function URLCheck(URL:String;Pause:Integer; At:Byte):Boolean;
Var A:Byte;
Label L1;
begin
ResponseCode:= -100;
A:=1;
L1:
try
result:=true;
//IdHTTP1.ConnectTimeout:=100;
//IdHTTP1.ReadTimeout:=50;
IdHTTP1.HTTPOptions := IdHTTP1.HTTPOptions + [hoNoProtocolErrorException];
IdHTTP1.Head(URL);
result := IdHTTP1.Response.ResponseCode = 200;
ResponseCode:=IdHTTP1.Response.ResponseCode;
except
result:=False;
end;
If A<AT Then begin
Sleep(Pause);AT:=AT+1;
Goto L1;
end;
end;
2 Имеет ли в принципе смысл делать "Чтение потока из сети по байтам"?
Как-то так:
- Код: Выделить всё
// HttpGetBinary2 старая самоделка на основе synapse
ms:=TMemoryStream.Create;
If HttpGetBinary2(URL,'80', ms) then
begin
ms.Seek(0, soFromBeginning);
Tmp:=MS.ReadByte;// Читаю только один байт
end ;
finally
Result:=False;
ms.Free;
end;
st.Free;
Как верно обустроить потоковое чтение данных при чтении ОДНОГО файла (нтмл кода или картинки ) из сети?
Мой временный "чит-код" выглядит довольно криво и не вызывает доверия даже у меня самого :
- Код: Выделить всё
Const
T_End:Boolean=True;
Var
ffIM: Timage;
CTime:TDateTime;
// поток LoadPicThread_3
Type
TLoadPicThread_3=Class(TThread)
private
protected
procedure Execute; override;
procedure Load;
procedure SLoad;
public
UPDATE :Boolean;
fIM: Timage;
fURL:String;
IsTO,IsInternet:Boolean;
constructor Create(CreateSuspended: boolean;URL:
String;Var Im: Timage);
end;
procedure TLoadPicThread_3.Load;
Var
B:TBitmap;
begin
try
B:=RE_NetLoadBMP_PHP(fURL);// "Обычное" чтение картинки .
if Not FreeOnTerminate then begin
if b.Modified then fIm.Picture.Bitmap.Assign(B);
fIm.Picture.Bitmap.Modified:=b.Modified;
end;
B.Free;
except
end;
end;
procedure TLoadPicThread_3.SLoad;
begin
IsInternet:= IsInternetConnected ;
end;
procedure TLoadPicThread_3.Execute;
begin
while (not Terminated) do
If UPDATE then begin
Synchronize(@SLoad);
if IsInternet then Load;
if not T_End then
T_End:=True;
UPDATE :=False;
FreeOnTerminate := True;
if IsTO Then fIm.Free;
Terminate;
end;
end;
constructor TLoadPicThread_3.Create(CreateSuspended: boolean;
URL:String;Var Im: Timage);
begin
UPDATE := False;
fIM:=im;
ffIM:=im;
fUrl:=Url;
T_End:= False;
FreeOnTerminate := False;
IsTO:=False;
inherited Create(CreateSuspended);
end;
Type
TWProc=Procedure (Im: Timage);
Var
WProc:TWProc;
Timer_NTT:TLoadPicThread_3;
CTime:TDateTime;
FOutTime:TDateTime;
procedure Tform1.Timer4Timer(Sender: TObject);
begin
Timer4.Enabled:=False;
if not T_End then begin
If (now-CTime)*10e4 >=FOutTime then begin
Timer_NTT.IsTO:=True;
WProc(Nil);
Exit;
end;
Timer4.Enabled:=True;
end else begin
WProc(ffIm);
ffIm.Free;
Timer_NTT.Terminate;
end;
end;
Сама функция чтения .
- Код: Выделить всё
Function Thread_RE_NetLoadBMP_PHP(URL:String;OutTime:TDateTime=1.0;
PWProc:Pointer=Nil;
H:integer=0;W:integer=0;
PHP:Boolean=false):TBitmap;
var
NTT:TLoadPicThread_3;
IM:TImage;
begin
Result:=nil;
If not T_End then exit;
IM:=TImage.Create(Nil);
If PWProc<> NIL then
begin
If FSetup.Timer4.Enabled then exit;
WProc:=TWProc( PWProc );
Timer_NTT:=TLoadPicThread_3.Create(True,{False} URL, Im);
Timer_NTT.Start;
CTime:=Now;Timer_NTT.UPDATE:=true;
FOutTime:=OutTime;
FSetup.Timer4.Enabled:=True;
Exit;
end;
NTT:=TLoadPicThread_3.Create(True,{False} URL, Im);
NTT.Start;
CTime:=Now;NTT.UPDATE:=true;
While not T_End do begin
If (now-CTime)*10e4 >=OutTime then begin
NTT.IsTO:=True;
Exit;
end else NTT.Resume;
Application.ProcessMessages;
end;
Result:=TBitmap.Create;
Result.Assign(Im.Picture.Bitmap);
NTT.Terminate;
IM.Free;
end;
У нее есть два режима
1 Простой "с ожиданием"
BB:=Thread_RE_NetLoadBMP_PHP(S);
2 С "отложенным чтением" ( с проверкой по таймеру )
- Код: Выделить всё
Procedure TestIMG (Im:TImage);far;
begin
...
If Im = Nil Then exit; // если таймаут
...
im.Free;
...
end;
...USetUp01.Thread_RE_NetLoadBMP_PHP(S,1.0,@TestIMG);
В общем понятно, что логика этой поделки малость хромает и есть множество возможностей "чему-то пойти не так", однако, пока лучше ничего не придумал .