В данном посте посмотрим, как разделить файл на части (так называемые chunks), передать его на сервер и собрать там обратно. Я использую Indy.
idHTTPServer
idHTTP
Для начала опишу общую идею. Тайминг работы программы, отображенный в логе примерно следующий
Если файл меньше размера чанка, то тогда получится так…
Вообще, я разрабатывал компонент для работы по HTTP протоколу, для просмотра, удаления, отправки файлов на сервер и др., по пути попалась эта задача. Она показалась мне нетривиальной. Думаю, многие программисты её решали, и каждый её решал по своему, я лишь выкладываю свою версию. Мало ли кому пригодится)
Длительность каждого блока кода я измерял с помощью конструкции
1 2 3 4 5 6 7 8 9 10 11 12 |
var iCounterPerSec: TLargeInteger; C1, C2: TLargeInteger; ... QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); // your code here QueryPerformanceCounter(C2); FContentSL.Add('1 Preparing temp dirs for chunks on Server and Client = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' s |
При загрузке на сервер, имя файла я переделывал с помощью GUID, для того, чтобы была контролируемая длина.
Исходники
Эта такая большая и нетривиальная задача, что проще смотреть и изучать исходники, кто пойдет по моим следам, вот держите. Я работал в Delphi Seattle
373_ps_httpclient_dev (исправлена ошибка с отправкой файлов с русскими именами)
Проектная группа состоит из 3 частей
Сервер – простое VCL приложение с компонентом idHTTPServer
Клиент – компонент, созданный на основе TPanel, с idHTTP на борту
Ну и тестовая программа, на которую погружается разрабатываемый компонент
Общий алгоритм
-Подготовить директории для временных файлов на клиенте и сервере
-Создать новое, уникальное имя, основанное на GUID
-Разделить файл на чанки при помощи TFileStream
-Подготовить информационный файл (число чанков, их имена и др.) и отправить его на сервер
-Отправлять по 1 чанку и получать положительный ответ. Если положительного ответа не получено – программа прекращает отправку и прерывается.
-Запустить таймер, который будет с какой-то периодичностью проверять – все ли чанки дошли до сервера, при положительном ответе инициировать сборку файла. При сборке, чанки сначала сортируются согласно номеру в имени, и лишь после этого начинается сборка файла.
-Удалить временные директории
По ходу дела также идет логирование времени выполнения операций.
Структура модулей
Пример использования
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
procedure TVisualFrame_HTTP.bSendFileInChunksClick(Sender: TObject); var SendFileInChunks:TSendFileInChunks; begin if OpenDialog.Execute then begin SendFileInChunks:=TSendFileInChunks.Create(Self); SendFileInChunks.SendFileInChunks( OpenDialog.FileName, 1048576); // 1024*1024 <<<ChunkSize); end; end; |
Код основной процедуры
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
procedure TSendFileInChunks.SendFileInChunks(AFilePath: string; ChunkSize: Int64); var dirTempClient:string; dirTempServer:Ansistring; DivideAndGatherFile:TDivideAndGatherFile; NewUniqueName: string; FileName:string; FolderName:string; HTTPRequests:THTTPRequests; FileSizeBytes:Int64; SplittedString: TArray<String>; FilesSDA:TStringdynArray; AppPath: string; i: Integer; outputDirOnServer: string; dayLogDirOnClient:string; dayLogFilePathOnClient:string; ChunksInfoFileName:string; MyServerResponse: string; Error:string; ResponseText:string; iCounterPerSec: TLargeInteger; C1, C2: TLargeInteger; StandartServerResponse: string; DateTime:TDateTime; NChunks: integer; begin // Measuring Total Time On Client QueryPerformanceFrequency(FiCounterPerSec); QueryPerformanceCounter(FC1_Total); // Preparation - begin QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); //Create HTTPRequests:=THTTPRequests.Create(Self); FileName:=ExtractFileName(AFilePath); FFileName:=FileName; HTTPRequests.IsHTTPConnectionOk(Error,ResponseText); FileSizeBytes:=GetFileSize(AFilePath); FileName:=ExtractFileName(AFilePath); // Exctracting SplittedString:=FileName.Split(['.']); if Length(SplittedString)>1 then FolderName:=SplittedString[High(SplittedString)-1] else FolderName:=FileName; NewUniqueName:=TCreateUniqueName.CreateUniqueNameAddingGUID(FileName,10); FNewUniqueName:=NewUniqueName; // to pass it to timer... // TFile.AppendAllText(dayLogFilePathOnClient,Content); // doesn't work well try //Creating dirs... outputDirOnServer:=CreateOutputDirOnServer; FoutputDirOnServer:=outputDirOnServer; // to pass it to timer... //Creating only Temp Folder On Server CreateOnlyTempFolder; dayLogDirOnClient:=CreateLogDirAndFileOnClient(dayLogFilePathOnClient); FdayLogFilePathOnClient:=dayLogFilePathOnClient; if TFile.Exists(dayLogFilePathOnClient) then try FContentSL.LoadFromFile(dayLogFilePathOnClient); except on E:Exception do raise Exception.Create('Error Message '+E.ClassName+' '+E.Message); end; //Logging DateTime:=now; FContentSL.Add(' '); FContentSL.Add('----------SENDING FILE IN CHUNKS----------'); FContentSL.Add(' '); FContentSL.Add('Filename= '+FileName); FContentSL.Add(' '); FContentSL.Add('FilenameOnServer= '+NewUniqueName); FContentSL.Add(' '); FContentSL.Add('DateTime='+DateTimeToStr(DateTime)); FContentSL.Add(' '); FContentSL.Add('---ClientSide---'); FContentSL.Add(' '); QueryPerformanceCounter(C2); FContentSL.Add(' Preparation = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); // Preparation - end if GetFileSize(AFilePath)< ChunkSize then begin //---------------Sending whole file here { DONE : Finish with if GetFileSize(AFilePath)< ChunkSize } QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); HTTPRequests. SendOneFile(AFilePath,FoutputDirOnServer,FNewUniqueName); QueryPerformanceCounter(C2); FContentSL.Add('Sending one whole file with fileSize<chunkSize = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); // Start checking if file on server tIsFileOnServer.Enabled:=true; end else begin //------------1 Preparing temp dirs for chunks on Server and Client QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); dirTempClient:=CreateTempDirOnClient(AFilePath,NewUniqueName); FTempDirForChunksClient:=dirTempClient; dirTempServer:=CreateTempDirOnServer; FTempDirForChunksServer:=dirTempServer; // to pass it to timer... QueryPerformanceCounter(C2); FContentSL.Add('1 Preparing temp dirs for chunks on Server and Client = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); //------2 Divide file in chunks in that dir QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); DivideAndGatherFile:=TDivideAndGatherFile.Create(Self); DivideAndGatherFile.DivideFileInChunks2( // Error handling Ok here AFilePath, ChunkSize, dirTempClient, NewUniqueName ); QueryPerformanceCounter(C2); // How many chunks? if ( GetFileSize(AFilePath) mod ChunkSize )<>0 then NChunks:=( GetFileSize(AFilePath) div ChunkSize )+1 else NChunks:=( GetFileSize(AFilePath) mod ChunkSize ); FContentSL.Add('2 Divide file in chunks in that dir = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'+ ' Number of chunks = '+NChunks.ToString); //---- * means requests to Server Side //*-----3 Sending ChunksInfo QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); ChunksInfoFileName:='ChunksInfo.txt'; FChunksInfoFileName:=ChunksInfoFileName; HTTPRequests. SendChunksInfoInFile(dirTempClient,dirTempServer, FileSizeBytes,ChunksInfoFileName); // Error Handling Ok QueryPerformanceCounter(C2); FContentSL.Add('3 Sending ChunksInfo = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); //*-----4 Sending Chunks QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); try FilesSDA:=TDirectory.GetFiles(dirTempClient); except on E:Exception do raise Exception.Create('Error in TDirectory.GetFiles(dirTempClient) '+ 'Class='+E.ClassName+'Message='+E.Message); end; HTTPRequests.SendManyFiles2(dirTempClient,dirTempServer); // Error Handling OK QueryPerformanceCounter(C2); FContentSL.Add('4 Sending Chunks = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); //---Timer that would check chunks integrity on Server Every second //---if integrity Ok - lets stop timer and gather chunks in file //*----5 Gather File from Chunks On Server tCheckChunksIntegrity.Enabled:=True; // Starting check integrity timer // TFile.AppendAllText(dayLogFilePathOnClient,ContentSL.Text); // doesn't work well end; finally //Freeing FreeAndNil(DivideAndGatherFile); FreeAndNil(HTTPRequests); end; end; |
Я наверное дам некоторые комментарии, а основной разбор, конечно будет для Вас по исходникам.
Итак, поехали, сверху вниз, слева направо.
HTTPRequests это модуль, в котором собраны запросы на сервер. К примеру, проверку соединения с сервером, я делаю таким образом
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
{Checking if HTTPServer Connection Ok} function THTTPRequests.IsHTTPConnectionOk(var Error, ResponseText: string): boolean; begin with VisualFrame_HTTP_UnitVar do begin Result:=false; try IdHTTP.Get(HTTPServerToRequest+'/testConnection'); // //for example like http://localhost:40000 if IdHTTP.ResponseCode=200 then// ResponseText='HTTP/1.1 200 OK' then Result:=true; // <<<< If Succeded //Also lets write Response Text ResponseText:=IdHTTP.ResponseText; except on E:Exception do begin ResponseText:=IdHTTP.ResponseText; raise Exception.Create('Server Connection Error '+ E.ClassName + 'error'+E.Message); //ShowMessage(E.ClassName + 'error'+E.Message ); end; end; end; end; |
На idHTTPServer, соответственно, код ответа выглядит следующим образом, в CommandGet
1 2 3 4 5 6 7 8 |
if ARequestInfo.URI='/testConnection' then begin // AResponseInfo.ContentText := 'ok'; AResponseInfo.ResponseNo := 200; AResponseInfo.WriteContent; end |
Я пока только начал разбираться с HTTP, поэтому многих деталей могу не знать, но этот вариант у меня работал, и пока я его оставил.
Далее, нам понадобится функция, для измерения размера файла, я использовал следующую
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
function TSendFileInChunks.GetFileSize(AFilePath: String): Int64; var FS: TFileStream; begin try Result:=-1; FS := TFileStream.Create(AFilePath, fmOpenRead); try Result:=FS.Size; finally FS.Free; end; except on E:EStreamError do begin Result := -1; raise Exception.Create('This is EStreamError EClassName'+E.ClassName+' ' +'EMessage '+E.Message); end; on E:Exception do begin Result := -1; raise Exception.Create(E.ClassName+' Exception Raised : ' +#13#10+#13#10+E.Message); end; end; end; |
В зависимости от того, больше или меньше размер файла размера чанка, программа будет ветвиться. Если размер чанка больше, тогда файл отправляется целиком, если меньше, файл делится на чанки, и отправляется каждый чанк.
Нам также нужно создать уникальное имя на сервере. Чтобы не думать об этом, я полагаюсь на GUID. Кроме того, это имя должно быть ограничено в длине. Я использовал комбинированный подход – оставляя часть имени, скажем, 10 символов, и добавляя GUID. Вот метод для создания нового уникального имени, на основе старого
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
{Creating New Unique Name with a rest of old name, using GUID} class function TCreateUniqueName.CreateUniqueNameAddingGUID(FileName: string; MaxOriginNameLength: Integer): string; var Extension:string; SplittedString: TArray<String>; SomeStringToChange: string; newguid:tguid; i: Integer; FileNameTemp:string; begin //Checks if FileName='' then Exit; //if MaxOriginNameLength=0 then exit; // if MaxOriginNameLength=0 will be only GUID as a result //1--------- First of all we need to extract extension if it is SplittedString:=FileName.Split(['.']); //we suppose that extensions are symbols after last '.', so... // if FileName has extension like 'SomeFileName.exe' if Length(SplittedString)>0 //1 and more, for example somename.ext [somename,ext] then begin Extension:=SplittedString[ High(SplittedString) ]; //Lets join everything except extension for i := Low(SplittedString) to High(SplittedString)-1 do begin if i=0 then SomeStringToChange:=SomeStringToChange+SplittedString[i] else SomeStringToChange:=SomeStringToChange+'.'+SplittedString[i]; end; //Cutting name up to the MaxOriginNameLength if Length( SomeStringToChange )> MaxOriginNameLength then SomeStringToChange:=SomeStringToChange.Substring(0,MaxOriginNameLength); //Adding GUID Createguid(newguid); SomeStringToChange:=SomeStringToChange+newguid.ToString; // Joining Extension SomeStringToChange:=SomeStringToChange+'.'+Extension; Result:=SomeStringToChange; end else // if FileName without Extension like 'SomeFileName' if Length(SplittedString)=0 then begin FileNameTemp:=FileName; //Cutting name up to the MaxOriginNameLength if Length( FileNameTemp )> MaxOriginNameLength then FileNameTemp:=FileNameTemp.Substring(0,MaxOriginNameLength); //Adding GUID Createguid(newguid); FileNameTemp:=FileNameTemp+newguid.ToString; Result:=FileNameTemp; end; //ShowMessage(Result); end; |
Файлы я решил хранить на сервере в следующей структуре.
При загрузке файла на сервер
1 |
AppPath\public\files\SomeUser_ID123\2016\11\11 |
То есть, конечная папка, это папка какого-то пользователя за конкретный день. Кроме того, есть папка temp, для создания подпапок чанков, для каждого загружаемого файла
1 |
AppPath\public\files\SomeUser_ID123\2016\11\11\temp |
То есть, в момент работы программы, это будет выглядеть примерно так…
После успешной загрузки – и сборки исходного файла из чанков – эта папка будет полностью удалена.
Соответственно функции для создания временных папок выглядят так…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
function TSendFileInChunks.CreateOnlyTempFolder:string; var Year: Word; Month: Word; Day: Word; Filename: string; dirTempServer: string; MyServerResponse:string; begin DecodeDate(Now, Year, Month, Day); // Filename := ExtractFileName(AFilePath); with VisualFrame_HTTP_UnitVar do begin dirTempServer := 'public' +'\'+ 'files' + '\' + HTTP_ConnectionParams.User + '_ID' + HTTP_ConnectionParams.IDUser + '\' + Year.ToString + '\' + Month.ToString + '\' + Day.ToString + '\' + 'temp'; end; Result:=dirTempServer; FHTTPRequests.DeleteDirOnServer(dirTempServer,MyServerResponse); HTTPRequests.CreateDirOnServer(dirTempServer,MyServerResponse); end; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
function TSendFileInChunks.CreateTempDirOnServer:string; var Year: Word; Month: Word; Day: Word; Filename: string; dirTempServer: string; // HTTPRequests:THTTPRequests; MyServerResponse:string; begin DecodeDate(Now, Year, Month, Day); // Filename := ExtractFileName(AFilePath); with VisualFrame_HTTP_UnitVar do begin dirTempServer := 'public' +'\'+ 'files' + '\' + HTTP_ConnectionParams.User + '_ID' + HTTP_ConnectionParams.IDUser + '\' + Year.ToString + '\' + Month.ToString + '\' + Day.ToString + '\' + 'temp' + '\' + FNewUniqueName+'\'; end; Result:=dirTempServer; FHTTPRequests.DeleteDirOnServer(dirTempServer,MyServerResponse); HTTPRequests.CreateDirOnServer(dirTempServer,MyServerResponse); end; |
Также нам понадобится временная папка на клиенте, для создания в ней чанков из отправляемого файла
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
function TSendFileInChunks.CreateTempDirOnClient(AFilePath: string; NewUniqueName:string):string; var Year: Word; Month: Word; Day: Word; Filename: string; dirTempClient: string; begin try DecodeDate(Now, Year, Month, Day); // Filename := ExtractFileName(AFilePath); with VisualFrame_HTTP_UnitVar do begin dirTempClient := ExtractFilePath(Application.Exename) + 'files' + '\' + HTTP_ConnectionParams.User + '_ID' + HTTP_ConnectionParams.IDUser + '\' + Year.ToString + '\' + Month.ToString + '\' + Day.ToString + '\' + 'temp' + '\' + NewUniqueName+'\'; end; Result:=dirTempClient; if not TDirectory.Exists(dirTempClient) then TDirectory.CreateDirectory(dirTempClient) else begin //deleting TDirectory.Delete(dirTempClient,true); //creating TDirectory.CreateDirectory(dirTempClient); end; except on E:Exception do raise Exception.Create('Error creating directory on client EClassName'+E.ClassName+' ' +'EMessage '+E.Message); end; end; |
И ещё понадобится папка и файл для логов, пока решил хранить их в папке каждого отдельного дня, то есть так…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
function TSendFileInChunks.CreateLogDirAndFileOnClient(var daylogfilepath:string):string; var Year: Word; Month: Word; Day: Word; Filename: string; dayLogDir: string; begin try DecodeDate(Now, Year, Month, Day); // Filename := ExtractFileName(AFilePath); with VisualFrame_HTTP_UnitVar do begin dayLogDir := ExtractFilePath(Application.Exename) + 'files' + '\' + HTTP_ConnectionParams.User + '_ID' + HTTP_ConnectionParams.IDUser + '\' + Year.ToString + '\' + Month.ToString + '\' + Day.ToString + '\' + 'log'; end; Result:=dayLogDir; if not TDirectory.Exists(dayLogDir) then TDirectory.CreateDirectory(dayLogDir); daylogfilepath:=dayLogDir+'\daylog.txt'; // if not TFile.Exists(daylogfilepath) then // TFile.Create(daylogfilepath); except on E:Exception do raise Exception.Create('Error creating log dir or log file '+E.ClassName+' ' +'EMessage '+E.Message); end; end; |
Preparation
Следующую часть основного кода, я назвал Preparation, здесь получается размер файла, готовятся директории на клиенте и сервере
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
// Preparation - begin QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); //Create HTTPRequests:=THTTPRequests.Create(Self); FileName:=ExtractFileName(AFilePath); FFileName:=FileName; HTTPRequests.IsHTTPConnectionOk(Error,ResponseText); FileSizeBytes:=GetFileSize(AFilePath); FileName:=ExtractFileName(AFilePath); // Exctracting SplittedString:=FileName.Split(['.']); if Length(SplittedString)>1 then FolderName:=SplittedString[High(SplittedString)-1] else FolderName:=FileName; NewUniqueName:=TCreateUniqueName.CreateUniqueNameAddingGUID(FileName,10); FNewUniqueName:=NewUniqueName; // to pass it to timer... // TFile.AppendAllText(dayLogFilePathOnClient,Content); // doesn't work well try //Creating dirs... outputDirOnServer:=CreateOutputDirOnServer; FoutputDirOnServer:=outputDirOnServer; // to pass it to timer... //Creating only Temp Folder On Server CreateOnlyTempFolder; dayLogDirOnClient:=CreateLogDirAndFileOnClient(dayLogFilePathOnClient); FdayLogFilePathOnClient:=dayLogFilePathOnClient; if TFile.Exists(dayLogFilePathOnClient) then try FContentSL.LoadFromFile(dayLogFilePathOnClient); except on E:Exception do raise Exception.Create('Error Message '+E.ClassName+' '+E.Message); end; //Logging DateTime:=now; FContentSL.Add(' '); FContentSL.Add('----------SENDING FILE IN CHUNKS----------'); FContentSL.Add(' '); FContentSL.Add('Filename= '+FileName); FContentSL.Add(' '); FContentSL.Add('FilenameOnServer= '+NewUniqueName); FContentSL.Add(' '); FContentSL.Add('DateTime='+DateTimeToStr(DateTime)); FContentSL.Add(' '); FContentSL.Add('---ClientSide---'); FContentSL.Add(' '); QueryPerformanceCounter(C2); FContentSL.Add(' Preparation = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); // Preparation - end ... |
Sending one whole file if its size < chunkSize
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
if GetFileSize(AFilePath)< ChunkSize then begin //---------------Sending whole file here { DONE : Finish with if GetFileSize(AFilePath)< ChunkSize } QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); HTTPRequests. SendOneFile(AFilePath,FoutputDirOnServer,FNewUniqueName); QueryPerformanceCounter(C2); FContentSL.Add('Sending one whole file with fileSize<chunkSize = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); // Start checking if file on server tIsFileOnServer.Enabled:=true; end else |
Суть этой части кода в чем? Отправляем файл на сервер и запускаем таймер, который шлет запросы на сервер – а не пришел ли файл на сервер? Конечно, тут можно было обойтись более простым способом – дождаться ответа сервера, но я сделал так как сделал, потому что это более универсально, если скажем, нужно отправлять несколько запросов на сервер, и ждать, пока все они выполнятся – тут лучше централизовать это ожидание. Я сделал это через таймер. И кроме того, отправка файла может затянуться на время, большее времени ожидания ответа от сервера.
Таймер проверки наличия файла на сервере
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
procedure TSendFileInChunks.tIsFileOnServerTimer(Sender: TObject); var TimeLimit:Int64; MyServerResponse:string; iCounterPerSec: TLargeInteger; C1, C2: TLargeInteger; FileToDownload:string; ms:TMemoryStream; SomeSL:TStringList; FOutputDirOnServerChanged:string; begin QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); TimeLimit:=24*60*60; // limiting in 24 hours... tIsFileOnServer.Tag:=tIsFileOnServer.Tag+1; // Check IsFileOnServer every inerval, 1000 milisec. FHTTPRequests.IsFileOnServer(FFileName,FoutputDirOnServer,MyServerResponse); if MyServerResponse='ok' then begin tIsFileOnServer.Enabled:=False; try QueryPerformanceCounter(C2); FContentSL.Add('IsFileOnServer = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); //Saving log //Loading Server Part of Log SomeSL:=TStringList.Create; ms:=TMemoryStream.Create; with VisualFrame_HTTP_UnitVar do begin FOutputDirOnServerChanged:= StringReplace(FoutputDirOnServer,'\','/',[rfReplaceAll]); with HTTP_ConnectionParams do begin FileToDownload:= //Protocol+'://'+Host+':'+Port+'/'+FTempDirForChunksServerChanged+'daylogServer.txt'; Protocol+'://'+Host+':'+Port+'/'+FOutputDirOnServerChanged+'/temp/'+FNewUniqueName+'_UploadlogServer.txt'; end; IdHTTP.Get(FileToDownload,ms); ms.Position:=0; SomeSL.LoadFromStream(ms); FContentSL.Add(SomeSL.Text); end; FreeAndNil(SomeSL); FreeAndNil(ms); // Adding Total Time to Log FContentSL.Add(' '); QueryPerformanceCounter(FC2_Total); FContentSL.Add('Total time (Measured on Client) = '+ FormatFloat('0.0000', (FC2_Total - FC1_Total) / FiCounterPerSec) + ' sec.'); // FContentSL.Add('-----------------------'); FContentSL.SaveToFile(FdayLogFilePathOnClient); //Delete tempLogFileOnServer FHTTPRequests.DeleteFileOnServer( FoutputDirOnServer+'\temp\'+FNewUniqueName+'_UploadlogServer.txt',MyServerResponse ); except on E:Exception do begin tIsFileOnServer.Enabled:=False; raise Exception.Create('Error '+ 'EClassName'+E.ClassName+'EMassage='+E.Message); end; end; end; QueryPerformanceCounter(C2); FContentSL.Add(' IsFileOnServer = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); end; |
Sending file, dividing on Chunks
Разберем следующую часть основного кода…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
begin //------------1 Preparing temp dirs for chunks on Server and Client QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); dirTempClient:=CreateTempDirOnClient(AFilePath,NewUniqueName); FTempDirForChunksClient:=dirTempClient; dirTempServer:=CreateTempDirOnServer; FTempDirForChunksServer:=dirTempServer; // to pass it to timer... QueryPerformanceCounter(C2); FContentSL.Add('1 Preparing temp dirs for chunks on Server and Client = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); //------2 Divide file in chunks in that dir QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); DivideAndGatherFile:=TDivideAndGatherFile.Create(Self); DivideAndGatherFile.DivideFileInChunks2( // Error handling Ok here AFilePath, ChunkSize, dirTempClient, NewUniqueName ); QueryPerformanceCounter(C2); // How many chunks? if ( GetFileSize(AFilePath) mod ChunkSize )<>0 then NChunks:=( GetFileSize(AFilePath) div ChunkSize )+1 else NChunks:=( GetFileSize(AFilePath) mod ChunkSize ); FContentSL.Add('2 Divide file in chunks in that dir = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'+ ' Number of chunks = '+NChunks.ToString); //---- * means requests to Server Side //*-----3 Sending ChunksInfo QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); ChunksInfoFileName:='ChunksInfo.txt'; FChunksInfoFileName:=ChunksInfoFileName; HTTPRequests. SendChunksInfoInFile(dirTempClient,dirTempServer, FileSizeBytes,ChunksInfoFileName); // Error Handling Ok QueryPerformanceCounter(C2); FContentSL.Add('3 Sending ChunksInfo = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); //*-----4 Sending Chunks QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); try FilesSDA:=TDirectory.GetFiles(dirTempClient); except on E:Exception do raise Exception.Create('Error in TDirectory.GetFiles(dirTempClient) '+ 'Class='+E.ClassName+'Message='+E.Message); end; HTTPRequests.SendManyFiles2(dirTempClient,dirTempServer); // Error Handling OK QueryPerformanceCounter(C2); FContentSL.Add('4 Sending Chunks = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); //---Timer that would check chunks integrity on Server Every second //---if integrity Ok - lets stop timer and gather chunks in file //*----5 Gather File from Chunks On Server tCheckChunksIntegrity.Enabled:=True; // Starting check integrity timer // TFile.AppendAllText(dayLogFilePathOnClient,ContentSL.Text); // doesn't work well end; |
Здесь важно понять, как разделить файл на чанки? Я сделал это так…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
procedure TDivideAndGatherFile. DivideFileInChunks2(AFilepath: string; AChunkSize: Int64; ADirForChunks: string; ANewFileNameWithGUID:string); var Buffer:TBytes; FS:TFileStream; MS:TMemoryStream; FileName:string; ChunkNumber:Int64; ByteToRead: Int64; ReadSize: Int64; CreateUniqueName:TCreateUniqueName; NewFileName: string; begin FS:=TFile.OpenRead(AFilepath); FS.Position:=0; MS:=TMemoryStream.Create; MS.Position:=0; FileName:=ExtractFileName(AFilepath); try try ByteToRead:=FS.Size; ChunkNumber:=0; while ByteToRead>0 do begin ReadSize := AChunkSize; if ReadSize > ByteToRead then ReadSize := ByteToRead; MS.CopyFrom(FS,ReadSize); NewFileName:=ANewFileNameWithGUID; NewFileName:='{chunk='+ChunkNumber.ToString+'}'; // TCreateUniqueName. // AddParamAndValueToName(NewFileName,'chunk',ChunkNumber.ToString); if Length(ADirForChunks+NewFileName)>MAX_PATH then // 255 or 260 raise Exception.Create('FileName '+FileName+' too long - try shorten it'); MS.SaveToFile(ADirForChunks+NewFileName); MS.Clear; MS.Position:=0; Inc(ChunkNumber); //ByteToRead:=ByteToRead-ReadSize; // or dec... dec(ByteToRead,ReadSize); end; finally FreeAndNil(FS); FreeAndNil(MS); end; except on E:EStreamError do raise Exception.Create('This is EStreamError EClassName'+E.ClassName+' ' +'EMessage '+E.Message); on E:Exception do raise Exception.Create('This is Error EClassName'+E.ClassName+' ' +'EMessage '+E.Message); end; end; |
Как собрать файл из чанков?
Мы предполагаем, что в директории находятся чанки, нет лишних файлов, и файлы, которые содержат в себе слово chunk будут использованы для соединения. Также, будем сортировать кусочки – ведь они могли придти в другом порядке.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
procedure TDivideAndGatherFileServer. GatherFileFromChunks(AChunksDir:string; AOutputDir: string; AOutputFilename:string ); var FilenamesWithChunksSDA:TStringDynArray; ChunkPathesSL:TStringList; ChunkNumbersSL:TStringList; ChunksPath:string; i: Integer; SLObjectList:TObjectList; SortStringList:TSortStringList; ChunkNumber: string; FS:TFileStream; AppServerPath:string; MS: TmemoryStream; Outputfilepath: string; begin //---------------------1 PREPARATION--------------------- //Create ChunkPathesSL:=TStringList.Create; ChunkNumbersSL:=TStringList.Create; SLObjectList:=TObjectList.Create; SortStringList:=TSortStringList.Create(Self); AppServerPath:=ExtractFilePath(Application.ExeName); //Gathering all files from directory try FilenamesWithChunksSDA:=TDirectory.GetFiles(AppServerPath+AChunksDir); except on E:Exception do raise Exception.Create('Error Message '+'Classname= ' + E.ClassName+' Message='+E.Message); end; // Filling String lists... ChunkPathesSL.Clear; for i := Low(FilenamesWithChunksSDA) to High(FilenamesWithChunksSDA) do begin if FilenamesWithChunksSDA[i].Contains('chunk') then begin ChunkPathesSL.Add(FilenamesWithChunksSDA[i]); ChunkNumber:= TCreateUniqueName.GetParamValueFromFileName(FilenamesWithChunksSDA[i],'chunk' ); ChunkNumbersSL.Add(ChunkNumber); end; end; //IntegrityCheck - check chunks and their sizes - done externally //Sorting SLObjectList.Add(ChunkPathesSL); SLObjectList.OwnsObjects:=False; // Sorting at last SortStringList.SortOneOrderManyStringLists( ChunkNumbersSL, SLObjectList, 'int64', true ); //------------------------2 GATHERING------------------- {so after all preparations we suggest that fileChunks pathes in ChunkPathesSL } AppServerPath:=ExtractFilePath(Application.ExeName); try if not TDirectory.Exists(AppServerPath+AOutputDir) then TDirectory.CreateDirectory(AppServerPath+AOutputDir); except on E:Exception do raise Exception.Create('Error Message '+'Classname= '+ E.ClassName+' Message='+E.Message); end; Outputfilepath:=AppServerPath+AOutputDir+'\'+AOutputFilename; FS:=TFile.Create(Outputfilepath); FS.Position:=0; MS:=TMemoryStream.Create; try try for i := 0 to ChunkPathesSL.Count-1 do begin MS.LoadFromFile(ChunkPathesSL[i]); FS.CopyFrom(MS,MS.Size); MS.Clear; MS.Position:=0; end; except on E:EStreamError do raise Exception.Create('This is EStreamError EClassName'+E.ClassName+' ' +'EMessage '+E.Message); on E:Exception do raise Exception.Create('This is Error EClassName'+E.ClassName+' ' +'EMessage '+E.Message); end; finally FreeAndNil(FS); FreeAndNil(MS); end; //Freeing FreeAndNil(ChunkPathesSL); FreeAndNil(ChunkNumbersSL); FreeAndNil(SLObjectList); FreeAndNil(SortStringList); end; |
Как отправить информацию о чанках на сервер?
Нам нужен некоторый файл, который бы содержал в себе информацию о том, какие чанки вообще должны быть получены на сервере. Этот файл я назвал ChunksInfo.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//*-----3 Sending ChunksInfo QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); ChunksInfoFileName:='ChunksInfo.txt'; FChunksInfoFileName:=ChunksInfoFileName; HTTPRequests. SendChunksInfoInFile(dirTempClient,dirTempServer, FileSizeBytes,ChunksInfoFileName); // Error Handling Ok QueryPerformanceCounter(C2); FContentSL.Add('3 Sending ChunksInfo = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); |
Вот процедура сбора и отправки информации о чанках. Отправка происходит в формате JSON, обратно на сервере происходит чтение этого формата.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
procedure THTTPRequests. SendChunksInfoInFile(AChunksDir:string; AchunksDirOnServer:string;AFileSize:int64; ChunksInfoFileName:string); var ChunksSDA:TStringDynArray; JsonObject:TJsonObject; NumberOfChunksAtAll: Integer; i: Integer; TempFilename:string; CurrentChunkNumber:string; ChunksInfoString: string; ChunksInfoPath:string; Error:string; ResponseText:string; PostData: TIdMultiPartFormDataStream; MyServerResponse:string; begin //-------------1 Gather info about chunks ChunksSDA:=TDirectory.GetFiles(AChunksDir); NumberOfChunksAtAll:=Length(ChunksSDA); JSONObject:=TJSONObject.Create; JSONObject.AddPair( (TJSONPair.Create('ChunksNumber',NumberOfChunksAtAll.ToString )) ); JSONObject.AddPair( (TJSONPair.Create('FileSize',AFileSize.ToString )) ); for i := 0 to High(ChunksSDA) do begin TempFilename:=ExtractFileName(ChunksSDA[i]); if not TempFilename.Contains('chunk') then Continue; // All filenames should contain chunk word CurrentChunkNumber:= TCreateUniqueName.GetParamValueFromFileName(TempFilename,'chunk'); // No errror handling here - could be troubles JSONObject.AddPair( (TJSONPair.Create(TempFilename,CurrentChunkNumber )) ); end; ChunksInfoString:=JSONObject.ToString; ChunksInfoPath:=AChunksDir+'\'+ChunksInfoFileName;//'ChunksInfo.txt'; TFile.WriteAllText(ChunksInfoPath,ChunksInfoString); //-------------2 Sending if not IsHTTPConnectionOk(Error,ResponseText) then Exit; with VisualFrame_HTTP_UnitVar do begin begin PostData := TIdMultiPartFormDataStream.Create; try idHTTP.Request.Referer := HTTPServerToRequest+'/SendChunksInfo'; // 'http://localhost:40000/sendfile'; // http://www.link.net/download'; idHTTP.Request.ContentType := 'multipart/form-data'; PostData.AddFormField('Login', 'SomeLogin'); PostData.AddFormField('Password', 'SomePassword'); // PostData.AddFormField('ChunksInfoString', ChunksInfoString); PostData.AddFormField('AchunksDirOnServer', AchunksDirOnServer); PostData.AddFile('attach', ChunksInfoPath, 'application/x-rar-compressed'); // PostData.AddFormField('action', 'post'); MyServerResponse:=idHTTP.Post(HTTPServerToRequest+'/SendChunksInfo', PostData); if MyServerResponse<>'ok' then raise Exception.Create('Server Response '+MyServerResponse); Application.ProcessMessages; finally if(Assigned(PostData)) then PostData.Free; // ShowMessage('idHTTP Sent OK'); end; end; end; end; |
Отправка чанков на сервер
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
//*-----4 Sending Chunks QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); try FilesSDA:=TDirectory.GetFiles(dirTempClient); except on E:Exception do raise Exception.Create('Error in TDirectory.GetFiles(dirTempClient) '+ 'Class='+E.ClassName+'Message='+E.Message); end; HTTPRequests.SendManyFiles2(dirTempClient,dirTempServer); // Error Handling OK QueryPerformanceCounter(C2); FContentSL.Add('4 Sending Chunks = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
procedure THTTPRequests.SendManyFiles2(AbsDirOnClient:string; AbsDirOnServer: string); var PostData: TIdMultiPartFormDataStream; Error:string; StandartServerResponse:string; MyServerResponse:string; i: integer; FilesSDA:TStringDynArray; AppPath: string; begin //------------1 GAthering chunk files from dir on Client FilesSDA:=TDirectory.GetFiles(AbsDirOnClient); with VisualFrame_HTTP_UnitVar do begin if not IsHTTPConnectionOk(Error,StandartServerResponse) then Exit; for i := 0 to High(FilesSDA) do begin MyServerResponse:=''; StandartServerResponse:=''; SendOneFile(FilesSDA[i],AbsDirOnServer,StandartServerResponse,MyServerResponse); end; end; end; |
Таймер проверки и сборки чанков
После всего, нам нужно запустить таймер, который бы проверял – все ли чанки дошли. И когда это окажется так – отправить запрос на сборку файла.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
procedure TSendFileInChunks.tCheckChunksIntegrityTimer(Sender: TObject); var MyServerResponse:string; MyServerResponseDelete:string; TimeLimit:Int64; iCounterPerSec: TLargeInteger; C1, C2: TLargeInteger; FileToDownload:string; ms:TMemoryStream; SomeSL:TStringList; FTempDirForChunksServerChanged:string; begin QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(C1); TimeLimit:=24*60*60; // limiting in 24 hours... tCheckChunksIntegrity.Tag:=tCheckChunksIntegrity.Tag+1; // Check integrity every inerval, 1000 milisec. FHTTPRequests.CheckChunksIntegrity(FTempDirForChunksServer,MyServerResponse,FChunksInfoFileName); if MyServerResponse='ok' then begin tCheckChunksIntegrity.Enabled:=False; FHTTPRequests.GatherFileFromChunks(FTempDirForChunksServer,FoutputDirOnServer,FNewUniqueName); // Error Handling Ok //deleting temp dir for chunks on Client { DONE : deleting temp dir for chunks on Client } try if TDirectory.Exists(FTempDirForChunksClient) then TDirectory.Delete(FTempDirForChunksClient,true); QueryPerformanceCounter(C2); FContentSL.Add('5 CheckIChunksIntegrityTimer = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); //Saving log //Loading Server Part of Log SomeSL:=TStringList.Create; ms:=TMemoryStream.Create; with VisualFrame_HTTP_UnitVar do begin FTempDirForChunksServerChanged:= StringReplace(FTempDirForChunksServer,'\','/',[rfReplaceAll]); with HTTP_ConnectionParams do begin FileToDownload:= Protocol+'://'+Host+':'+Port+'/'+FTempDirForChunksServerChanged+'UploadlogServer.txt'; end; IdHTTP.Get(FileToDownload,ms); ms.Position:=0; SomeSL.LoadFromStream(ms); FContentSL.Add(SomeSL.Text); end; FreeAndNil(SomeSL); FreeAndNil(ms); // Adding Total Time to Log FContentSL.Add(' '); QueryPerformanceCounter(FC2_Total); FContentSL.Add('Total time (Measured on Client) = '+ FormatFloat('0.0000', (FC2_Total - FC1_Total) / FiCounterPerSec) + ' sec.'); // FContentSL.Add('-----------------------'); FContentSL.SaveToFile(FdayLogFilePathOnClient); except on E:Exception do begin tCheckChunksIntegrity.Enabled:=False; raise Exception.Create('Error deleting dir '+ 'EClassName'+E.ClassName+'EMassage='+E.Message); end; end; QueryPerformanceCounter(C2); FContentSL.Add('5 CheckIChunksIntegrityTimer = '+ FormatFloat('0.0000', (C2 - C1) / iCounterPerSec) + ' sec.'); //deleting temp dir for chunks on Server // FHTTPRequests.DeleteDirOnServer(FTempDirForChunksServer,MyServerResponseDelete); end; if tCheckChunksIntegrity.Tag>TimeLimit then begin tCheckChunksIntegrity.Enabled:=False; raise Exception.Create('Time to upload file expired '); end; end; |
Проверка целостности чанков
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
procedure THTTPRequests. CheckChunksIntegrity(AdirOnServer:string; var AMyServerResponse:string;AChunksInfoFileName:string); var PostData: TIdMultiPartFormDataStream; Error:string; ResponseText:string; begin with VisualFrame_HTTP_UnitVar do begin begin if not IsHTTPConnectionOk(Error,ResponseText) then Exit; PostData := TIdMultiPartFormDataStream.Create; try idHTTP.Request.Referer := HTTPServerToRequest+'/checkChunksIntegrity'; idHTTP.Request.ContentType := 'multipart/form-data'; PostData.AddFormField('Login', 'SomeLogin'); PostData.AddFormField('Password', 'SomePassword'); PostData.AddFormField('dirOnServer', AdirOnServer); PostData.AddFormField('ChunksInfoFileName', AChunksInfoFileName); AMyServerResponse:= idHTTP.Post(HTTPServerToRequest+'/checkChunksIntegrity', PostData); if AMyServerResponse<>'ok' then raise Exception.Create('Server Response '+AMyServerResponse); Application.ProcessMessages; finally if(Assigned(PostData)) then PostData.Free; // ShowMessage('idHTTP Sent OK'); // StandartServerResponse:=IdHTTP.Response.ResponseText;//IdHTTP.ResponseText; end; end; end; end; |
Пока не этом остановлюсь. Будут вопросы – пишите.