Saved Sqlite Image Appears Totally Black On Uploaded Server Database - delphi

I have an interesting problem. I have to take a signature image that is saved in Sqlite3 database.
When you view the saved signature on the Sqlite3 database it is a signature. The database is uploaded to a server that has MSSQL Server which then reads the signature image. Unfortunately this image appears totally black on the server and it comes back the same. The db administrator says that the data appears to be garbage (includes unicode characters).
Example of the image data is:
Sent Data: Tried to include but unfortunately it will not show unicode. It is thousands of bytes long.
Returned Data: *System.Byte[]* <<and that is it - 26 bytes long.
My guess is that the unicode is responsible. Not sure how to resolve this issue.
The data connection component is TSQLConnection. The query component is TSQLQuery. I am using XE5 building a Firemonkey iOS mobile application.
Here is my code. Appreciate any help.
function SaveSig: boolean;
var
fStream: TMemoryStream;
begin
Result := False;
fStream := TMemoryStream.Create;
try
try
fStream.Seek(0, soFromBeginning);
fStream.Position := 0;
if Assigned(imgSig) then
begin
imgSig.Bitmap.SaveToStream(fStream);
Result := SqlInsertSig(fStream);
end;
except
on e: Exception do
ShowMessage(ERROR_BITMAP + e.Message);
end;
finally
if Assigned(fStream) then
FreeAndNil(fStream);
end;
end;
function SqlInsertSig(const ms: TMemoryStream): boolean;
begin
Result := False;
try
try
sq.Active := False;
sq.CommandText := 'Insert Into Signatures (Id, Sig) Values (' +
QuotedStr(IntToStr(Id)) + ', :sig)';
sq.Params.ParamByName('sig').LoadFromStream(ms, ftBlob);
Result := (sq.ExecSQL > 0);
except
on e: Exception do
MessageDlg(e.Message, TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], 0);
end;
finally
sq.Active := False;
end;
end;

Related

Reading HTML content from Clipboard in Delphi

I have a webpage which has various tables on it. These tables are Javascript components, not just pure HTML tables. I need to process the text of this webpage (somewhat similar to screen scraping) with a Delphi program (Delphi 10.3).
I do a Ctrl-A/Ctrl-C to select all the webpage and copy everything to the clipboard. If I paste this into a TMemo component in my program, I am only getting text outside the table. If I paste into MS Word, I can see all the content, including the text inside the table.
I can paste this properly into TAdvRichEditor (3rd party), but it takes forever, and I often run out of memory. This leads me to believe that I need to directly read the clipboard with an HTML clipboard format.
I set up a clipboard HTML format. When I inspect the clipboard contents, I get what looks like all Kanji characters.
How do I read the contents of the clipboard when the contents are HTML?
In a perfect world, I would like ONLY the text, not the HTML itself, but I can strip that out later. Here is what I am doing now...
On initialization.. (where CF_HTML is a global variable)
CF_HTML := RegisterClipboardFormat('HTML Format');
then my routine is...
function TMain.ClipboardAsHTML: String;
var
Data: THandle;
Ptr: PChar;
begin
Result := '';
with Clipboard do
begin
Open;
try
Data := GetAsHandle(CF_HTML);
if Data <> 0 then
begin
Ptr := PChar(GlobalLock(Data));
if Ptr <> nil then
try
Result := Ptr;
finally
GlobalUnlock(Data);
end;
end;
finally
Close;
end;
end;
end;
** ADDITIONAL INFO - When I copy from the webpage... I can then inspect the contents of the Clipboard buffer using a free tool called InsideClipBoard. It shows that the clipboard contains 1 entry, with 5 formats: CT_TEXT, CF_OEMTEXT, CF_UNICODETEXT, CF_LOCALE, and 'HTML Format' (with Format ID of 49409). Only 'HTML Format' contains what I am looking for.... and that is what I am trying to access with the code that I have shown.
The HTML format is documented here. It is placed on the clipboard as UTF-8 encoded text, and you can extract it like this.
{$APPTYPE CONSOLE}
uses
System.SysUtils,
Winapi.Windows,
Vcl.Clipbrd;
procedure Main;
var
CF_HTML: Word;
Data: THandle;
Ptr: Pointer;
Error: DWORD;
Size: NativeUInt;
utf8: UTF8String;
Html: string;
begin
CF_HTML := RegisterClipboardFormat('HTML Format');
Clipboard.Open;
try
Data := Clipboard.GetAsHandle(CF_HTML);
if Data=0 then begin
Writeln('HTML data not found on clipboard');
Exit;
end;
Ptr := GlobalLock(Data);
if not Assigned(Ptr) then begin
Error := GetLastError;
Writeln('GlobalLock failed: ' + SysErrorMessage(Error));
Exit;
end;
try
Size := GlobalSize(Data);
if Size=0 then begin
Error := GetLastError;
Writeln('GlobalSize failed: ' + SysErrorMessage(Error));
Exit;
end;
SetString(utf8, PAnsiChar(Ptr), Size - 1);
Html := string(utf8);
Writeln(Html);
finally
GlobalUnlock(Data);
end;
finally
Clipboard.Close;
end;
end;
begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.

Saved data from TBlobField is corrupted for lengths >= 100KB

I'm modifying a program that is written in Delphi 6.0
I have a table in Oracle with a BLOB column named FILE_CONTENT.
I have already managed to upload an XML File that is about 100 KB. I have verified that the file content was correctly uploaded using SQL Developer.
The problem I have is when I try to download back the file content from DB to a file. This is an example code I'm using to donwload it:
procedure TfrmDownload.Save();
var
fileStream: TFileStream;
bField: TBlobField;
begin
dmDigital.qrGetData.Open;
dmDigital.RequestLive := True;
bField := TBlobField(dmDigital.qrGetData.FieldByName('FILE_CONTENT'));
fileStream := TFileStream.Create('FILE.XML', fmCreate);
bField.SaveToStream(fileStream);
FlushFileBuffers(fileStream.Handle);
fileStream.Free;
dmDigital.qrGetData.Close;
end;
The previous code already downloads the file content to FILE.XML. I'm using RequestLive:=True to be able to download a large BLOB (otherwise the file content is truncated to 32K max)
The resulting file is the same size as the original file. However, when I compare the downloaded file with the original one there are some differences (for example the last character is missing and other characters are also changed), therefore it seems to be a problem while downloading the content.
Do you know what cuould be wrong?
The problem seems to be related to Delphi code because I already tried with C# and the file content is downloaded correctly.
Don't use TBlobField.SaveToStream() directly, use TDataSet.CreateBlobStream() instead (which is what TBlobField.SaveToStream() uses internally anyway):
procedure TfrmDownload.Save;
var
fileStream: TFileStream;
bField: TField;
bStream: TStream;
begin
dmDigital.qrGetData.Open;
try
dmDigital.RequestLive := True;
bField := dmDigital.qrGetData.FieldByName('FILE_CONTENT');
bStream := bField.DataSet.CreateBlobStream(bField, bmRead);
try
fileStream := TFileStream.Create('FILE.XML', fmCreate);
try
fileStream.CopyFrom(bStream, 0);
FlushFileBuffers(fileStream.Handle);
finally
fileStream.Free;
end;
finally
bStream.Free;
end;
finally
dmDigital.qrGetData.Close;
end;
end;
TDataSet.CreateBlobStream() allows the DataSet to decide the best way to access the BLOB data. If the returned TStream is not delivering the data correctly, then either the TStream class implementation that CreateBlobStream() uses is broken, or the underlying DB driver is buggy. Try taking CopyFrom() out of the equation so you can verify the data as it is being retrieved:
procedure TfrmDownload.Save;
const
MaxBufSize = $F000;
var
Buffer: array of Byte;
N: Integer;
fileStream: TFileStream;
bField: TField;
bStream: TStream;
begin
dmDigital.qrGetData.Open;
try
dmDigital.RequestLive := True;
bField := dmDigital.qrGetData.FieldByName('FILE_CONTENT');
bStream := bField.DataSet.CreateBlobStream(bField, bmRead);
try
fileStream := TFileStream.Create('FILE.XML', fmCreate);
try
//fileStream.CopyFrom(bStream, 0);
SetLength(Buffer, MaxBufSize);
repeat
N := bStream.Read(PByte(Buffer)^, MaxBufSize);
if N < 1 then Break;
// verify data here...
fileStream.WriteBuffer(PByte(Buffer)^, N);
until False;
FlushFileBuffers(fileStream.Handle);
finally
fileStream.Free;
end;
finally
bStream.Free;
end;
finally
dmDigital.qrGetData.Close;
end;
end;

How to correctly download csv without 'save as' dialog' using TWebbrowser and Delphi?

I subscribe to a secure https web page containing a button that downloads some data as csv. I am trying to automate the download without the 'save as' dialog appearing but always seem to get an empty file downloaded. I suspect it has something to do with file type I'm using with IdHttp as most of my code works correctly.
Please can anyone help with my use of IdHttp or see where else I am going wrong?
The download button on the site calls some javascript to perform the download as follows
<a class="dlCSV" href="javascript:void(0);" onclick="dl_module.DownloadCsv();return false;">Download in CSV format…</a>
In Delphi I use a TWeb browser to log on securely and navigate to the page.
Clicking the download button in the TwebBrowser by hand shows the 'save as' dialog and then correctly downloads the csv data, defaulting to the filename 'data.csv'.
Automating clicking the button using execScript (below) also works, again showing the 'save as' dialog and correctly downloading the data with the same default filename.
procedure TForm1.BtnClickDownloadbuttonClick(Sender: TObject);
var TheDocument : IHTMLDocument2; // current HTML document
HTMLWindow: IHTMLWindow2; // parent window of current HTML document
begin
TheDocument := WebBrowser1.Document as IHTMLDocument2; // Get reference to current document
if not Assigned(TheDocument) then
Exit;
HTMLWindow := TheDocument.parentWindow; // Get parent window of current document
if Assigned(HTMLWindow) then
try
HTMLWindow.execScript('dl_module.DownloadCsv()', 'JavaScript'); // execute JS function to do download
except
on E : Exception do
begin
showmessage ('Exception class name = '+E.ClassName+ slinebreak
+ 'Exception message = '+E.Message);
end //on E
end;
end;
Then I added TLama's code from here How do I keep an embedded browser from prompting where to save a downloaded file? to use IDownloadManager to intercept the download and prevent the 'save as' dialog. This is where it seems to go wrong as I then get an empty file downloaded, and not with the name data.csv.
My code for function TWebBrowser.Download, TWebBrowser.InvokeEvent, function TWebBrowser.QueryService and TForm1.FormCreate are identical to that provided by TLama in the link above.
My procedure TForm1.Button1Click is the same except that I changed the download function being called to the one on my page by changing the line
HTMLWindow.execScript('SRT_stocFund.Export()', 'JavaScript');
to
HTMLWindow.execScript('dl_module.DownloadCsv()', 'JavaScript');
and my procedure TForm1.BeforeFileDownload is identical except that because I'm on a secure site I added the variable
var
LHandler: TIdSSLIOHandlerSocketOpenSSL; //<< on a https site
and after creating the Filestream I added the lines
LHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
IdHTTP.IOHandler := LHandler;
The issue seems to be in procedure TForm1.BeforeFileDownload where I note that the value of FileSource is
https://www.the_web_site_name/Ashx/GenericCSV.ashx.
There is a short delay while IdHTTP.Get(FileSource, FileStream); executes and then a file is created on my hard disc but called 'GenericCSV.ashx' (not data.csv) and the file is zero bytes long and completely empty.
Any ideas why its not downloading the file called data.csv (Do I somehow have to execute GenericCSV.ashx as well? if so how?)
For info here is my version of procedure TForm1.BeforeFileDownload
procedure TForm1.BeforeFileDownload(Sender: TObject; const FileSource: WideString; var Allowed: Boolean);
var
IdHTTP: TIdHTTP;
FileTarget: string;
FileStream: TMemoryStream;
LHandler: TIdSSLIOHandlerSocketOpenSSL; // added as its a https site
begin
FileSourceEdit.Text := FileSource;
Allowed := ShowDialogCheckBox.Checked;
if not Allowed then
try
IdHTTP := TIdHTTP.Create(nil);
try
FileStream := TMemoryStream.Create;
LHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil); //<<< added as its a https site
IdHTTP.IOHandler := LHandler; //<<< added as its a https site
try
IdHTTP.HandleRedirects := True;
IdHTTP.Get(FileSource, FileStream);
FileTarget := IdHTTP.URL.Document;
if FileTarget = '' then
FileTarget := 'File';
FileTarget := ExtractFilePath(ParamStr(0)) + FileTarget;
FileStream.SaveToFile(FileTarget);
finally
FileStream.Free;
end;
finally
IdHTTP.Free;
end;
ShowMessage('Downloading finished! File has been saved as:' + sLineBreak +
FileTarget);
except
on E: Exception do
ShowMessage(E.Message);
end;
end;
After you login, you can use this code to retrieve cookies from TWebBrowser
procedure GetHttpOnlyCookie(const AUrl: string; var ACookies: string);
const
INTERNET_COOKIE_HTTPONLY = 8192;
var
i: Integer;
hModule: THandle;
InternetGetCookieEx: function(lpszUrl, lpszCookieName, lpszCookieData
: PAnsiChar; var lpdwSize: DWORD; dwFlags: DWORD; lpReserved: pointer)
: BOOL; stdCall;
CookieSize: DWORD;
CookieData: PAnsiChar;
begin
LoadLibrary('wininet.dll');
hModule := GetModuleHandle('wininet.dll');
if (hModule <> 0) then
begin
#InternetGetCookieEx := GetProcAddress(hModule, 'InternetGetCookieExA');
if (#InternetGetCookieEx <> nil) then
begin
CookieSize := 1024;
Cookiedata := AllocMem(CookieSize);
try
if InternetGetCookieEx(PAnsiChar(AUrl), nil, Cookiedata, CookieSize, INTERNET_COOKIE_HTTPONLY, nil) then
begin
ACookies:=CookieData;
end;
finally
FreeMem(Cookiedata);
end;
end;
end;
end;
Then you just parse your cookies and add them (you have to create CookieManager in IdHTTP first)
IdHTTP1.CookieManager.AddServerCookie();
Then you start your download and it should work if you passed all parameters correctly (unfortunately, it is not possible to find out what your site requires).
Thank you smooty86 but I think its time I gave up trying to doing it this way and simply parse the page I can see.
I don't mind trying to understand code and adapting it to my needs but its so much harder trying to follow hints and suggestions when I'm working in the dark and especially don't know what parameters are needed everywhere. (I'm not daft, I've been programming for nearly 30 years and have spent over 4 years developing this particular data processing application but rarely touch web stuff)
However, the progress so far is...
Running your GetHttpOnlyCookie code after a successful login using automated filling in of the fields and clicking the login button returned an empty string so I used this code instead that at least seemed to return something that looked a little similar to your cookie string, ie seveveral strings separated by semicolons, most being name=value. (IdCookieManager1 is connected to IdHttp)
CookieList := Tstringlist.Create ;
try
CookieList.Delimiter := ';' ;
document := WebBrowser1.Document as IHTMLDocument2;
CookieList.DelimitedText := document.cookie;
for i := 0 to CookieList.Count-1 do
IdCookieManager1.AddCookie(CookieList[i],LOGIN_URL)
finally
CookieList.Free;
end;
Then in my original procedure BeforeFileDownload I try to log IdHttp into the site as well using code I adapted from here Log in to website from Delphi and the the cookies held in the cookie manager.
Displaying the string returned showed lots of HTML that appeared to represent the oringinal log in page and not the page you see after log in
procedure TFrmInportGrades.BeforeFileDownload(Sender: TObject; const FileSource: WideString; var Allowed: Boolean);
var
FileTarget: string;
FileStream: TMemoryStream;
request : Tstringlist;
s : string;
begin
FileSourceEdit.Text := FileSource;
Allowed := ShowDialogCheckBox.Checked;
if not Allowed then
begin
try
FileStream := TMemoryStream.Create;
IdHTTP.CookieManager := IdCookieManager1;
s := LogInIdHttp; //<<<< log in the IdHttp
showmessage(s); //<<<< debug
IdHTTP.Get(FileSource, FileStream);
FileTarget := IdHTTP.URL.Document;
if FileTarget = '' then
FileTarget := 'File';
FileTarget := ExtractFilePath(ParamStr(0)) + FileTarget;
FileStream.SaveToFile(FileTarget);
finally
FileStream.Free;
end;
ShowMessage('Downloading finished! File has been saved as:' + sLineBreak +
FileTarget);
end;
end;
The login code I used is below but I don't really know what I am doing here or what needs to be put into the Request.Add() parameters. I used 'Inspect element' from firefox to get the name of the user and password boxes and put the correct users name and password after the '=' sign in lines {3} and {4}. In lines {2},{6} and {7} I put the url of the log in site. I've no idea what lines {1}, {2}, {5} do or even if they are correct or necessary
function TFrmInportGrades.LogInIdHttp: string;
var
Request: TStringList;
Response: TMemoryStream;
LHandler: TIdSSLIOHandlerSocketOpenSSL; // added as its a https site
begin
Result := '';
try
Response := TMemoryStream.Create;
try
Request := TStringList.Create;
try
{1} Request.Add('op=login');
{2} Request.Add('redirect=https://www.thewebsite.com/Login.aspx' );
{3} Request.Add('ctl00$ctl00$Body$Body$loginManager$ctl00$loginEmailInput=usernme');
{4} Request.Add('ctl00$ctl00$Body$Body$loginManager$ctl01$passwordInput=password'});
LHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil); //<<< added as its a https site
IdHTTP.IOHandler := LHandler; //<<< added as its a https site
IdHTTP.AllowCookies := True;
IdHTTP.HandleRedirects := True;
{5} IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
{6} IdHTTP.Post('https://www.thewebsite.com/Login.aspx', Request, Response);
{7} Result := IdHTTP.Get('https://www.thewebsite.com/Login.aspx');
finally
Request.Free;
end;
finally
Response.Free;
end;
except
on E: Exception do
ShowMessage(E.Message);
end;
end;
The net result of all this is that I don't get a file created at all now, not even a zero byte one. This all seems very overcomplicated simply to avoid or automate the 'Save As' dialog and is requiring lots of code that I won't be able to maintan afterwards. Unless somebody has a simpler solution I'll just parse what I can see (BTW I tried TEmbeddedWebBrowser but there is so little documentation for it I couldn't see how to make it download correctly. Might try again later.) Thank you for trying to help!

Store Picture to Blob Using Firebird with UIBQuery in Delphi

I have components:
1 OpenPictureDialog (to open picture)
2 Edit (edtID & edtName)
1 Button (to save record)
I used UIBQuery to insert a new record, including picture in one section.
Here is my code:
with UIBQuery1 do
try
SQL.Clear;
SQL.Add('INSERT INTO EMPLOYEE');
SQL.Add('(ID, NAME, PIC)');
SQL.Add('VALUES');
SQL.Add('(:ID, :NAME, :PIC)');
params.AsInteger[0] := StrToInt(edtID.Text);
params.AsString[1] := edtName.Text;
// How to give a param for blob here?
Execute;
Transaction.Commit;
bsSkinMessage1.MessageDlg2('Has been saved.','New Record',mtInformation,[mbok],0);
except
Transaction.RollBack;
raise;
end;
I succeed show the record including picture from database, but I have no clue to store the picture into database in one click.
How to doing this? Is it possible to give parameter of picture in query?
Someone knows about UIB please..
I change my code but I get an access violation:
procedure TchfEmployee.btnSaveClick(Sender: TObject);
var
ms: TMemoryStream;
begin
ms := TMemoryStream.Create;
Image2.Picture.Bitmap.SaveToStream(ms);
ms.Position:=0;
begin
with UIBQuery1 do
try
SQL.Clear;
SQL.Add('INSERT INTO EMPLOYEE');
SQL.Add('(ID, NAME, PIC)');
SQL.Add('VALUES');
SQL.Add('(:ID, :NAME, :PIC)');
params.AsString[0] := edtID.Text;
params.AsString[1] := edtName.Text;
TBLOBField(Params.ByNameAsString['PIC']).LoadFromStream(ms);
Execute;
Transaction.Commit;
bsSkinMessage1.MessageDlg2('Has been saved.','New Record',mtInformation,[mbok],0);
except
Transaction.RollBack;
raise;
end;
try
UIBDataSet1.Close;
UIBDataSet1.Open;
except
raise;
end;
end;
end;
How's exactly save my TImage to BLOB using UIBQuery? I am really in a heavy stucked.
Try this:
...
ParamsSetBlob('PIC', ms);
Execute;
...
Take a look at UIB source code. Look for TUIBStatement class.
You need to do the following :-
Var
lStream : TMemoryStream;
Begin
lStream := TMemoryStream.Create;
Try
Image2.Picture.SaveToStream(lStream);
lStream.Position := 0;
Query.ParamSetBlob('Pic', lStream);
Finally
FreeAndNil(lStream);
End;
End;

Is TIdHTTPServer Compatible with Microsoft BITS

We are trying to write an update server for our software using the TIdHTTPServer component. Currently we are serving an XML file that lists the available updates and their file versions etc.., when the client program finds a updated version it should start to download it using BITS.
Now this is where we have a problem, our programs are requesting the XML file and seeing there is an update available. It then creates a BITS job to download it, however BITS keeps reporting that the download failed. We can download the file using the same URL and IE/Firefox/Chrome.
so my question:
Is TIdHTTPServer compatible with BITS?
I ask this as I have discovered that there are these download requirements for bits to work.
HTTP Requirements for BITS Downloads
BITS supports HTTP and HTTPS downloads and uploads and requires that the server supports the HTTP/1.1 protocol. For downloads, the HTTP server's Head method must return the file size and its Get method must support the Content-Range and Content-Length headers. As a result, BITS only transfers static file content and generates an error if you try to transfer dynamic content, unless the ASP, ISAPI, or CGI script supports the Content-Range and Content-Length headers.
BITS can use an HTTP/1.0 server as long as it meets the Head and Get method requirements.
To support downloading ranges of a file, the server must support the following requirements:
Allow MIME headers to include the standard Content-Range and Content-Type headers, plus a maximum of 180 bytes of other headers.
Allow a maximum of two CR/LFs between the HTTP headers and the first boundary string.
Just found a bug in indy that prevents transfer of files over 2.1GB when using range requests.
here it is
IdHTTPHeaderInfo.pas aprox line 770
procedure TIdEntityRange.SetText(const AValue: String);
var
LValue, S: String;
begin
LValue := Trim(AValue);
if LValue <> '' then
begin
S := Fetch(LValue, '-'); {do not localize}
if S <> '' then begin
FStartPos := StrToIntDef(S, -1);
FEndPos := StrToIntDef(Fetch(LValue), -1);
FSuffixLength := -1;
end else begin
FStartPos := -1;
FEndPos := -1;
FSuffixLength := StrToIntDef(Fetch(LValue), -1);
end;
end else begin
FStartPos := -1;
FEndPos := -1;
FSuffixLength := -1;
end;
end;
This should be
procedure TIdEntityRange.SetText(const AValue: String);
var
LValue, S: String;
begin
LValue := Trim(AValue);
if LValue <> '' then
begin
S := Fetch(LValue, '-'); {do not localize}
if S <> '' then begin
FStartPos := StrToInt64Def(S, -1);
FEndPos := StrToInt64Def(Fetch(LValue), -1);
FSuffixLength := -1;
end else begin
FStartPos := -1;
FEndPos := -1;
FSuffixLength := StrToInt64Def(Fetch(LValue), -1);
end;
end else begin
FStartPos := -1;
FEndPos := -1;
FSuffixLength := -1;
end;
end;
One for Remy to fix
When you handle the OnCommandGet event, you are given a TIdRequestHeaderInfo, which descends from TIdEntityHeaderInfo; that contains all the headers the request contained, and it even parses out some header values to read as properties, including ContentRangeStart, ContentRangeEnd, and ContentLength.
You can use those properties to populate the stream that you assign to the TIdHTTPResponseInfo.ContentStream property. The entire stream will get sent.
It's your job to differentiate between GET and HEAD requests; OnCommandGet will get triggered either way. Check the IdHTTPRequestInfo.CommandType property.
So, although Indy may not support BITS, it provides all the tools you need to write a program that does support BITS.
So the answer to this question is:
Yes TIdHTTPServer is Bits Compatible.
But only if you are prepared to do the work yourself.
As suggested by #Rob Kennedy and Myself it is possible to read the headers and send the data back using the requested ranges, one chunk at a time.
Here is an example of what I am doing in the OnCommandGet event
procedure TForm3.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
Ranges : TIdEntityRanges;
DataChunk: TMemoryStream;
ReqFile: TFileStream;
ChunkLength: Int64;
Directory, FileName: string;
begin
Directory := 'H:';
case ARequestInfo.Ranges.Count of
0:
begin
//serve file normally
end;
1:
begin
//serve range of bytes specified for file
filename := Directory + ARequestInfo.Document;
if FileExists(FileName) then
begin
ReqFile := TFileStream.Create(FileName, fmOpenRead);
try
ChunkLength := Succ(ARequestInfo.Ranges.Ranges[0].EndPos - ARequestInfo.Ranges.Ranges[0].StartPos);
if ChunkLength > ReqFile.Size then
ChunkLength := ReqFile.Size;
DataChunk := TMemoryStream.Create;
DataChunk.Posistion := ARequestInfo.Ranges.Ranges[0].StartPos;
DataChunk.CopyFrom(ReqFile, ChunkLength);
AResponseInfo.ContentStream := DataChunk;
AResponseInfo.ContentType := IdHTTPServer1.MIMETable.GetFileMIMEType(FileName);
AResponseInfo.ContentRangeUnits := ARequestInfo.Ranges.Units;
AResponseInfo.ContentRangeStart := ARequestInfo.Ranges.Ranges[0].StartPos;
AResponseInfo.ContentRangeEnd := ARequestInfo.Ranges.Ranges[0].StartPos + Pred(ChunkLength);
AResponseInfo.ContentRangeInstanceLength := ReqFile.Size;
AResponseInfo.ResponseNo := 206;
finally
ReqFile.Free;
end;
end
else
AResponseInfo.ResponseNo := 404;
end
else
begin
//serve the file as multipart/byteranges
end;
end;
end;
This is by no means finished but it shows the basics of responding to the range requests from BITS. Most importantly it works.
Any comments on the code would be appreciated, constructive criticism always welcome.

Resources