Downloading a file from Amazon with Delphi - delphi

I am working with an API that provides me with the URL of a file and I wish to download the file to file or stream using Delphi XE.
The URL is in this form:-
https://xxxxxxx.s3-us-west-2.amazonaws.com/xxx/Reports/414_20160114021919.pdf?AWSAccessKeyId=xxxxxxxxxxxxxx&Expires=1478915858&response-content-disposition=attachment%3Bfilename%3D%22123%20Test%20St%20Dallas__2016_01_14_03_16_00.pdf%22&Signature=xxxxxxxxxxx%3D
where I have xxxxxxxxx'ed out the sensitive security stuff.
If I paste the URL into Chrome, it finds the file and brings up the save dialog.
In my Delphi Program I'm using URLMon with the following code (happily burgled and adapted from answers to similar questions here on SO) :-
function TdmXXXXXXXXX.DownloadFile(AURL, AExtWithDot: string): string;
// returns temp file name or empty string if failed;
var
sTempFileName: string;
iError: integer;
begin
sTempFileName := apmDM.GetTempFileName(AExtWithDot, True);
iError := UrlDownloadToFile(nil, PChar(AURL), PChar(sTempFileName), 0, nil);
if iError = S_OK then
Result := sTempFileName
else
Result := '';
showmessage(SysUtils.IntToHex(iError,8));
end;
The showmessage returns "800C000D"
In URLMon:-
$EXTERNALSYM INET_E_UNKNOWN_PROTOCOL}
INET_E_UNKNOWN_PROTOCOL = HResult($800C000D);
So I'm assuming that the problem relates to stuff after the "?" in the URL, "AWSAccessKeyId=" etc.
Not sure where to go from here - searched here and with Mr Google ...
Cheers
Jeff

Though you'll typically find pointers to the Indy TIdHTTP component, my personal favorite is including the Microsoft XML library (MSXML2_TLB), and using the XmlHTTPRequest component. If you use a TStreamAdapter on it's responseStream property, you'll have a stream to a resource over an URL.

I scratched all that I had done and restarted from scratch.
It all worked using URLMon and
URLDownloadToFile(nil, PChar(sReportURL), PChar(sTempFileName), 0, nil);
I have pored over the code and can't see any difference between reported problem code and my final code.

Related

Delphi: SetFileDate creates wrong LastWriteTime (Summer/Wintertime)

i am downloading a file from my server (i only get the bytes and a DateTime for the lastwritetime attribute) and after downloading the data i create a new file on my local machine and want to set the lastwritetime attribute.
For this i am using the following method:
procedure SetFileDate(const FileName: string; NewDate: TDateTime);
var
FileDate, FileHandle: Integer;
begin
try
FileDate := DateTimeToFileDate(NewDate);
FileHandle := FileOpen(FileName, fmOpenReadWrite or fmShareDenyWrite);
if FileHandle > 0 then
begin
FileSetDate(FileHandle, FileDate);
FileClose(FileHandle);
end;
except
begin
// ERROR Log
err.Msg('FileReqThrd.SetFileDate');
end;
end;
end;
For the 'NewDate' parameter i use the DateTime which i get from my server.
I tried to convert the DateTime from the server like this to get the valid lastwritetime (i am requesting the data from a WCF this is why i am converting it to UTCDateTime, the untouched data from the WCF service is TXSDateTime):
TDateTime cloudFileDateTime := StrToDateTime(DateTimeToStr(cloudDownloadResult.FileCloudData.Lastwritetime.AsUTCDateTime));
But in the end my lastwritetime attribute from files which have a lastwritetime in the wintertime period are wrong with -1h.
I hope you understand my problem and can give me an idea how to solve it.
Best regards
The easiest way to do this is to call TFile.SetLastWriteTimeUtc from the System.IOUtils unit.
TFile.SetLastWriteTimeUtc(FileName,
DateTimeUtc);
If this function is not available use the Win32 API function SetFileTime.
You'll also need DateTimeToSystemTime and then SystemTimeToFileTime in that scenario.
The answer provided by David (to use TFile.SetLastWriteTimeUtc) is correct. However, there was some discussion in the comments about bugs. As I am unable to comment (due to lack of rep), I'll add this here for anyone who comes across this problem in future.
While TFile.SetLastWriteTimeUtc works correctly, TFile.GetLastWriteTimeUtc does indeed have a bug relating to daylight saving time. There is a bug report filed with Embarcadero, and it looks like they've now fixed it in Delphi 10.3 Rio (though I haven't tried it yet).
If you are working with an older version of Delphi, you will have to work around the problem via use of the Windows API. e.g. GetFileAttributesEx:
function GetFileModTimeUtc(filePath: string): TDateTime;
var data: TWin32FindData;
var sysTime: TSystemTime;
begin
if GetFileAttributesEx(PChar(filePath), GetFileExInfoStandard, #data) and
FileTimeToSystemTime(data.ftLastWriteTime, sysTime) then begin
Result := SystemTimeToDateTime(sysTime);
end else begin
raise Exception.Create('Unable to get last file write time for ' + filePath);
end;
end;

Remove or edit ID3Tag version 2 from MP3 file using Delphi 7

I'm using both old good MPGTools and own, simple method of setting ID3 Tag in my MP3 files. But both approaches are too old to support ID3Tag version 2. I'm looking for any solution that would allow my application, written in Delphi 7, to either remove ID3Tag from each file it process or to set it to exactly the same values as ID3Tag version 1 is set.
Currently I'm removing ID3Tagv2 manually, using quick keyboard combination in Winamp.
I don't use v2 or album art or all these "new" addition, so the quickiest way to get rid of ID3Tagv2 (if it exists in particular file) would be all I need.
Of course I've tried to search the Internet using Google, but either I've got bad day or I'm asking wrong question, because all the results I'm getting on above mentioned questions are fake result from search engine stealers like Software Informer etc.
As it happens, one of my projects sitting here that is awaiting completion (about 80%, I'm more a hobbyist when it comes to Delphi and had more pressing stuff come up, then I found a program I was able to download which fit my requirements precisely) is a full ID3 tag editor for MP3 files. While v1 was super-easy, v2 is much harder. You can refer to the standard document for v2.3 here.
But I will confine myself to the points addressed here.
You might want ID3v2 tags depending on the application. My portable MP3 player only accepts v2 tags, which is what pushed me to do the project in the first place.
ID3v2 tags are written at the beginning of files in a variable length manner with variable numbers of tags which may or may not be present. Fortunately, the full length of the data should be in the first record if it's an ID3v2 tagged file. Hence, read the file locate the length of the ID3v2 data, then rewrite the file without the ID3v2 data and the tags are removed. Having the data at the beginning makes this necessary and is indeed a frustration. Anything I do in the future to the code would involve trying to change data in place. Some very dirty code follows, which AFAIR worked, but you will need to clean up if you use (I'm sure some here will be content to point out exactly how I should). But test it well just to be sure. Also be sure to ask if I missed anything from the unit I copied this out of (it's a 19.3KB pas file) that you would need:
type
sarray = array[0..3] of byte;
psarray = ^sarray;
ID3v2Header = packed record
identifier: array[0..2] of char;
major_version: byte;
minor_version: byte;
flags: byte;
size: DWord;
end;
function size_decodeh(insize: DWord): DWord;
{ decodes the size headers only, which does not use bit 7 in each byte,
requires MSB conversion as well }
var
outdval: DWord;
outd, ind: psarray;
tnext2, pnext2: byte;
begin
outdval := 0;
outd := #outdval;
ind := #insize;
tnext2 := ind^[2] shr 1;
pnext2 := ind^[1] shr 2;
outd^[0] := ind^[3] or ((ind^[2] and $01) shl 7);
outd^[1] := tnext2 or ((ind^[1] and $03) shl 6);
outd^[2] := pnext2 or ((ind^[0] and $07) shl 5);
outd^[3] := ind^[0] shr 3;
Result := outdval;
end;
procedure ID3v2_LoadData(filename: string; var memrec: pointer;
var outsize: integer);
{ procedure loads ID3v2 data from "filename". Returns outsize = 0 if
there is no ID3v2 data }
var
infile: file;
v1h: ID3V2Header;
begin
assign(infile, filename);
reset(infile, 1);
// read main header to get id3v2 size
blockread(infile, v1h, sizeof(v1h));
// detect if there is id3v2 data
if v1h.identifier = 'ID3' then
begin
outsize := size_decodeh(v1h.size);
// read ID3v2 header data
getmem(memrec, outsize);
blockread(infile, memrec^, outsize);
Close(infile);
end
else
outsize := 0;
end;
function id3v2_erase(infilestr: string): boolean;
{ erase all ID3v2 data. Data are stored at the beginning of file, so file
must be rewritten }
const
tempfilename = 'TMp#!0X.MP3';
var
memrec: pointer;
outsize, dataread: integer;
IsID3v2: boolean;
databuffer: array[1..32768] of byte;
newfile, origfile: file;
begin
// reuse service routine to get information
Id3v2_loaddata(infilestr, memrec, outsize);
// is there ID3v2 data?
if outsize > 0 then
begin
// need to clean up after the service routine
freemem(memrec);
// get amount of data to erase
outsize := outsize + sizeof(Id3v2Header);
writeln('Data to delete is: ', outsize, ' bytes.');
// now rewrite the file
AssignFile(origfile, infilestr);
reset(origfile, 1);
AssignFile(newfile, tempfilename);
rewrite(newfile, 1);
Seek(origfile, outsize);
repeat
blockread(origfile, databuffer, sizeof(databuffer), dataread);
blockwrite(newfile, databuffer, dataread);
until dataread = 0;
CloseFile(origfile);
CloseFile(newfile);
// rename temp file and delete original
DeleteFile(infilestr);
RenameFile(tempfilename, infilestr);
IsID3v2 := true;
end
else
IsID3v2 := false;
Result := IsID3v2;
end;
Full editing capability that works in most all situations is obviously a tougher hill to climb than that, but all the details are there in that document I linked to. Hopefully this helps you out.
There are few libs that works fine with ID3V2. Back in 2006 I did a big research to find Delphi library that supports most of the Id3V2 specification for Delphi 7.
And I found these 2:
Audio Tools Library (was the best for that moment). I think that it even could read/write tags in Unicode. Here is the unit Id3V2.pas
JVCL has component to work with Id3V2 tags. But it didn't had Unicode support for non-unicode Delphi in 2006.
Btw, if you do not use JVCL yet, it's not worth to install more than 600 components just to get Id3V2 support.
So, take a look at Audio Tools Library.

Delphi: How to load a text file on the internet into a string?

On my program I have a function that checks for the current version of the program, which it gets from the url: www.tablemaster.webs.com/versioninfo.txt
As you can see, at the URL is just plain text. I need to load this text from the URL into a string on my program, how will I go about doing this? I've searched around but found nothing..
PS: I need the simplest code possible..
Thanks in advance :)
I would use Indy's TIdHTTP with it's easiest GET overload this way:
uses
IdHTTP;
procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
IdHTTP: TIdHTTP;
begin
IdHTTP := TIdHTTP.Create(nil);
try
S := IdHTTP.Get('http://www.tablemaster.webs.com/versioninfo.txt');
ShowMessage(S);
finally
IdHTTP.Free;
end;
end;
you can use TIEHTTP component from myfxboard..
to load txt file from url with TIEHTTP:
http.ExecuteURL('www.tablemaster.webs.com/versioninfo.txt');
Memo1.Lines.Add(http.sl.Text);
I would use MSXML2_TLB, especially if I already use XML in the project. MSXML2 is present in Windows since version Internet Explorer 5.5, so chances are real good it's present on the system. You can get MSXML2_TLB.pas using the Import Type Library option from the main menu, and selecting "Microsoft XML, v6.0" (or higher) from the list.
var
r:XMLHTTP;
begin
r:=CoXMLHTTP.Create;
r.open('GET','http://www.tablemaster.webs.com/versioninfo.txt','','');
r.send(EmptyParam);
if r.status<>200 then raise Exception.Create(IntToStr(r.status)+' '+r.statusText);
Result:=r.responseText;
end;
uses
IdHTTP;
procedure TForm1.Button1Click(Sender: TObject);
var
.
.
it seems in this method system cached the txt file so if the file change on web this method denies changes.!

Delphi Download Video from the Internet using URLDownloadToFile

How can I implement the following Windows function in Delphi?
HRESULT URLDownloadToFile(
LPUNKNOWN pCaller,
LPCTSTR szURL,
LPCTSTR szFileName,
DWORD dwReserved,
LPBINDSTATUSCALLBACK lpfnCB
);
URLDownloadToFile Function: http://msdn.microsoft.com/en-us/library/ms775123(VS.85).aspx
The question that prompted me was asked here.
Downloading flv from youtube using curlpp on top of curl - video not playing
Regards, Pieter.
uses
URLMon, ShellApi;
function DownloadFile(SourceFile, DestFile: string): Boolean;
begin
try
Result := UrlDownloadToFile(nil, PChar(SourceFile), PChar(DestFile), 0, nil) = 0;
except
Result := False;
end;
end;
Without header file we can not know what is LPBINDSTATUSCALLBACK for example. The best approach is to google around if someone has already made a conversion of the whole header file. If there isn't one, then try some C to Delphi convertor (http://www.drbob42.com/delphi/headconv.htm, http://cc.embarcadero.com/item/26951). Beware that they can only convert 60-80% of the code, but hopefully part you are interested in will be converted. If you are still stuck after all this, then search for VB conversion of the header. It will be much easier then conversion from C.

Can I send request in PUT and DELETE in Delphi 7?

I need to send PUT and DELETE along with POST, GET to a REST API how can I do it?
Delphi 7 comes with Indy. See the TIdHTTP component and specificly the Get and Put methods.
Or look at the open source Synapse library. There are some simple function calls in the HTTPSend unit which make implementing this completely painless. Just use the sample functions/procedures as your model for the PUT/DELETE. The existing routines already supply the POST and GET. The difference is in the method passed.
Personally I have found this library to be perfectly matched for working with REST. Its simple, well written and easy to extend.
For example, here is a simple put that sends and receives a stream:
function HttpPutBinary(const URL: string; const Data: TStream): Boolean;
var
HTTP: THTTPSend;
begin
HTTP := THTTPSend.Create;
try
HTTP.Document.CopyFrom(Data, 0);
HTTP.MimeType := 'Application/octet-stream';
Result := HTTP.HTTPMethod('PUT', URL); // changed method from 'POST'
Data.Size := 0;
if Result then
begin
Data.Seek(0, soFromBeginning);
Data.CopyFrom(HTTP.Document, 0);
end;
finally
HTTP.Free;
end;
end;
Check out the ICS components, they are suitable for the job.

Resources