I have already tried code from StackOverflow, f.e. Download, pause and resume an download using Indy components.
But when I try to use IdHttp1.Disconnect() in order to pause download and then resume it, the download does not resume. The file is downloaded from zero. So, what should I do in order to add resume support to my Delphi app?
BTW: the server allows download resuming, because IDM can resume the download.
You have to check the size of the remote file, and the size of the local file (if exists).
If the size of the remote file is larger than the size of the local file, then you have to set the Ranges property of the Request property of TIdHTTP to the starting point (the local file size).
For example, if the remote file size is 100000 bytes, and the local file size is 80000 bytes, then you have to set http.Request.Ranges to start at 80000.
Here is a fully working code for you to use:
function FileSize(const FileName: string): Int64;
var
AttributeData: TWin32FileAttributeData;
begin
if GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, #AttributeData) then
begin
Int64Rec(Result).Lo := AttributeData.nFileSizeLow;
Int64Rec(Result).Hi := AttributeData.nFileSizeHigh;
end
else
Result := -1;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
http: TIdHTTP;
FS: TFileStream;
file_url: String;
file_path: String;
web_file_size: Integer;
local_file_size: Integer;
IhaveToDownload: Boolean;
begin
file_url := 'http://212.183.159.230/50MB.zip';
file_path := 'C:\50MB.zip';
IhaveToDownload := False;
FS := nil;
http := TIdHTTP.Create(nil);
try
//I will check if local file exists
if FileExists(file_path) then
begin
//I have to run http.Head() to get the ContentLength of the remote file.
http.Head(file_url);
//web_file_size now has the remote file size in bytes
web_file_size := http.Response.ContentLength;
//local_file_size now has the local file size in bytes
local_file_size := FileSize(file_path);
//check if remote file is bigger than local file (so I have to resume download)
if (web_file_size > local_file_size) and (http.Response.AcceptRanges = 'bytes') then
begin
//remote file is bigger than local file, so I will resume download.
http.Request.Ranges.Add.StartPos := local_file_size;
FS := TFileStream.Create(file_path, fmOpenReadWrite);
FS.Position := local_file_size;
IhaveToDownload := True;
end
else if web_file_size <> local_file_size then
begin
//remote file is not equal with local file, so I will restart download.
FS := TFileStream.Create(file_path, fmCreate);
IhaveToDownload := True;
end;
end
else
begin
//File does not exist locally. I will create a new local file
FS := TFileStream.Create(file_path, fmCreate);
IhaveToDownload := True;
end;
if IhaveToDownload then
http.Get(file_url, FS);
finally
FS.Free;
http.Free;
end;
end;
Related
I'm trying to build up an installer using Inno Setup. I need to setup a prerequisite installation for .NET Core framework. By far I'm able to detect .NET Core existence. But I'm having difficulty with executing the installation.
Here's the code I have by far:
[Files]
Source: "\\shared\path\dotnet-runtime-3.1.10-win-x64.exe"; DestDir: "{tmp}"; \
Flags: deleteafterinstall; BeforeInstall: InstallFramework; \
Check: FrameworkIsNotInstalled
[Code]
var
CancelWithoutPrompt: boolean;
InstallValue: Cardinal;
function InitializeSetup(): Boolean;
begin
CancelWithoutPrompt := false;
result := true;
end;
procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
if CurPageID=wpInstalling then
Confirm := not CancelWithoutPrompt;
end;
function FrameworkIsNotInstalled: Boolean;
begin
Result := not RegQueryDWordValue(HKEY_LOCAL_MACHINE,
'SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\x64\sharedfx\Microsoft.NETCore.App',
'3.1.10', InstallValue) or (InstallValue <> 1)
end;
procedure InstallFramework;
var
StatusText: string;
ResultCode: Integer;
begin
StatusText := WizardForm.StatusLabel.Caption;
WizardForm.StatusLabel.Caption := 'Installing .net core framework...';
WizardForm.ProgressGauge.Style := npbstMarquee;
try
if not Exec(ExpandConstant('{tmp}\dotnet-runtime-3.1.10-win-x64.exe'), '/q /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
// you can interact with the user that the installation failed
MsgBox('.NET Core installation failed with code: ' + IntToStr(ResultCode) + '.',
mbError, MB_OK);
CancelWithoutPrompt := true;
WizardForm.Close;
end;
finally
WizardForm.StatusLabel.Caption := StatusText;
WizardForm.ProgressGauge.Style := npbstNormal;
end;
end;
I'm getting result code 2 response from the installation, anyone could enlighten me what's code 2 error about? Or is that any way I could extract further detail on the error information? I tried empty argument and tried to search for Code 2 error information on the internet but still no luck.
I had tried with Shellexec and both return the same result
The installation directly using the .net exe are working fine locally
Any help or information would be much appreciate =)
You are trying to run the dotnet-runtime-3.1.10-win-x64.exe before you actually "install" it to the {tmp}:
BeforeInstall: InstallFramework
You have to use the AfterInstall parameter:
AfterInstall: InstallFramework
I'm trying to read serial data from an Arduino to my Mac (10.12.6). I have downloaded the Synapse library for FreePascal (Lazarus v.2.0.8) from here but I run into an error...
The Arduino is programmed using the Arduino IDE and sends random numbers (between 0 and 255) as a string to the serial port. I am trying to read these strings using FreePascal so that I can plot the values.
Following instructions here I have downloaded and used the Synapse library in the following way:
1) Uncompress the library folder
2) In Lazarus goto 'Project' -> 'Project Inspector' -> 'Add Files from File System' -> select 'synaser.pas'.
3) Add the following code to the form button event:
procedure TForm1.Button1Click(Sender: TObject);
var
ser: TBlockSerial;
begin
ser := TBlockSerial.Create;
try
ser.Connect('my-com-port'); // write here Arduino COM port number (on linux it's something like '/dev/ttyUSB0')
Sleep(250);
ser.config(9600, 8, 'N', SB1, False, False);
ser.SendString('on'); // button 2 should have 'off' here
finally
ser.free;
end;
end;
4) Press run.
An error message appears in the synaser.pas file:
Error incompatible types: got "ShortInt" expected "Pointer"..
Here's the part of the synaser.pas file referred to:
{$IFNDEF MSWINDOWS}
procedure TBlockSerial.Purge;
begin
{$IFNDEF FPC}
SerialCheck(ioctl(FHandle, TCFLSH, TCIOFLUSH));
{$ELSE}
{$IFDEF DARWIN}
SerialCheck(fpioctl(FHandle, TCIOflush, TCIOFLUSH)); { <------ here*******}
{$ELSE}
SerialCheck(fpioctl(FHandle, TCFLSH, Pointer(PtrInt(TCIOFLUSH))));
{$ENDIF}
{$ENDIF}
FBuffer := '';
ExceptCheck;
end;
I am using a Mac and this error seems to be related to a windows system?
Thanks #tonypdmtr.
I had to change the 'synaser.pas' file in the following way to get it to work. Bit of a hack I feel:
Change line 1939 to the following:
SerialCheck(fpioctl(FHandle, TCIOflush, Pointer(PtrInt(TCIOFLUSH))));
Comment out lines 2201, 2202 and 2204 in the same. This sounds like a bad way of doing it but I got it to work.
Changing the button event to the following code allows me to read a single line of data from the Arduino with each button click:
procedure TForm1.Button1Click(Sender: TObject);
var
ser: TBlockSerial;
begin
ser := TBlockSerial.Create;
try
ser.Connect('/dev/cu.wchusbserial1420'); // write here Arduino COM port number (on linux it's something like '/dev/ttyUSB0')
Sleep(250);
ser.config(9600, 8, 'N', SB1, False, False);
Label1.Caption := ser.RecvString(100);
finally
ser.free;
end;
end;
I feel the library should just work without having to change it.
In this situation, I need to install a file to specific directory, but in different computer it might be in different folder so I need to check which on is correct.
For example, I have a file and it needs to install in A folder or B folder or C folder, depends on the computer has A or B or C. So I need to check them first, if the computer has B, then install the file in the B folder, etc.
I know I can use check after file's DestDir, if the directory doesn't exist then it won't install anything, but what I need is install that file to other directory.
Thanks in advance.
In the InitializeSetup event function, check for existence of your pre-defined set of directories and remember the one you find. Then set the default installation path to the found one using a scripted constant in the DefaultDirName directive.
You will possible also want to set the DisableDirPage=yes and the UsePreviousAppDir=no.
[Setup]
DefaultDirName={code:GetDirName}
DisableDirPage=yes
UsePreviousAppDir=no
[Files]
Source: "MyProg.exe"; DestDir: "{app}"
Source: "MyProg.chm"; DestDir: "{app}"
[Code]
var
DirName: string;
function TryPath(Path: string): Boolean;
begin
Result := DirExists(Path);
if Result then
begin
Log(Format('Path %s exists', [Path]))
DirName := Path;
end
else
begin
Log(Format('Path %s does not', [Path]))
end;
end;
function GetDirName(Param: string): string;
begin
Result := DirName;
end;
function InitializeSetup(): Boolean;
begin
Result :=
TryPath('C:\path1') or
TryPath('C:\path2') or
TryPath('C:\path3');
if Result then
begin
Log(Format('Destination %s selected', [DirName]))
end
else
begin
MsgBox('No destination found, aborting installation', mbError, MB_OK);
end;
end;
Instead of using DefaultDirName={code:GetDirName}, you can also use DestDir: "{code:GetDirName}" in the respective entries of the [Files] section, if appropriate.
Usually, I use the following structure to send POST request with contents of varchar2 and numbers .. etc.
content := '{"Original File Name":"'||V_HOMEBANNER_1_EN_NAME(indx)||'"}';
url := 'https://api.appery.io/rest/1/db/Names';
req := utl_http.begin_request(url, 'POST',' HTTP/1.1');
UTL_HTTP.set_header(req, 'X-Appery-Database-Id', '5f2dac54b02cc6402dbe');
utl_http.set_header(req, 'content-type', 'application/json');
UTL_HTTP.set_header(req, 'X-Appery-Session-Token', sessionToken);
utl_http.set_header(req, 'Content-Length', LENGTH(content));
utl_http.write_text(req, content);
res := utl_http.get_response(req);
BEGIN
LOOP
utl_http.read_line(res, buffer);
END LOOP;
utl_http.end_response(res);
EXCEPTION
WHEN utl_http.end_of_body THEN
utl_http.end_response(res);
END;
And It works just fine. However, now I want to send/upload a blob files (images of jpg) into some MongoDB collection called 'Files' (so url := ttps://api.appery.io/rest/1/db/Files). The collection guide has the following cURL as a general advice :
curl -X POST \
-H "X-Appery-Database-Id: 5f2dac54b02cc6402dbe" \
-H "X-Appery-Session-Token: <session_token>" \
-H "Content-Type: <content_type>" \
--data-binary '<file_content>' \
https://api.appery.io/rest/1/db/files/<file_name>
But I could not translate this cURL into PL/SQL request. Specifically, the part (--data-binary '')
I have these BLOB files in Oracle table and they are stored with their names as follows:
+-----------+--------------+
| File_Name | File_content |
+-----------+--------------+
| PIC_1.jpg | BLOB |
| PIC_2.jpg | BLOB |
| PIC_3.jpg | BLOB |
+-----------+--------------+
My question, how to upload these images into the targeted URL?
Inspired by this blog advised by #JeffreyKemp, I go it working by only replacing write_text() with write_raw() in order to send content body as BLOB (Requested by the API).
The following code is the critical part of my function with the changes needed:
content := V_HOMEBANNER_1_EN(indx);
file_name := 'test.jpg';
url := 'https://api.appery.io/rest/1/db/files/'||file_name;
req := utl_http.begin_request(url, 'POST',' HTTP/1.1');
UTL_HTTP.set_header(req, 'X-Appery-Database-Id', '53fae4b02cc4021dbe');
UTL_HTTP.set_header(req, 'X-Appery-Session-Token', sessionToken);
utl_http.set_header(req, 'content-type', 'image/jpeg');
req_length := DBMS_LOB.getlength(CONTENT);
DBMS_OUTPUT.PUT_LINE(req_length);
--IF MSG DATA UNDER 32KB LIMIT:
IF req_length <= 32767
THEN
begin
utl_http.set_header(req, 'Content-Length', req_length);
utl_http.write_raw(req, content);
exception
when others then
DBMS_OUTPUT.PUT_LINE(sqlerrm);
end;
--IF MSG DATA MORE THAN 32KB
ELSIF req_length >32767
THEN
BEGIN
DBMS_OUTPUT.PUT_LINE(req_length);
utl_http.set_header(req, 'Transfer-Encoding', 'Chunked');
WHILE (offset < req_length)
LOOP
DBMS_LOB.read(content, amount, offset, buffer);
utl_http.write_raw(req, buffer);
offset := offset + amount;
END LOOP;
exception
when others then
DBMS_OUTPUT.PUT_LINE(sqlerrm);
end;
END IF;
l_http_response := UTL_HTTP.get_response(req);
UTL_HTTP.read_text(l_http_response, response_body, 32767);
UTL_HTTP.end_response(l_http_response);
Tried and tested for both greater and smaller than 32KB images/jpg.
How do I programatically get the name of an ODBC driver's DLL file for a given ODBC driver. For example, given "SQL Server Native Client 10.0" I want to find the name of that driver's DLL file: sqlncli10.dll. I can see this in REGEDIT in the "Driver" entry in the registry under HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI. If I try to read the value from the registry in my code it returns an empty string. I also tried using the ODBC API function SQLDrivers. The code below successfully returns all the values of the attributes in the Attribs variable except "Driver". Everything is there - APILevel, ConnectFunctions, CPTimeout, etc - but "Driver" is not in the list.
repeat
Status := SQLDrivers (HENV, SQL_FETCH_NEXT, PAnsiChar(DriverName), 255,
NameLen, PAnsiChar(Attribs), 1024, AttrLen);
if Status = 0 then begin
List.Add(DriverName);
List.Add(Attribs);
end;
until Status <> 0;
You can use SQLGetInfo() with InfoType=SQL_DRIVER_NAME
I hope this will look like:
Status := SQLGetInfo(ConnEnv, SQL_DRIVER_NAME, PAnsiChar(DriverName), 255, NameLen);
But this function works with already connected database.
I tried SQLDrives() and you are right: in my environment this function also do not return DLL name. So I tried to read it from registry and it worked this way:
DLLName := RegGetStringDirect(HKEY_LOCAL_MACHINE, 'SOFTWARE\ODBC\ODBCINST.INI\' + DriverName, 'Driver');
For driver: IBM INFORMIX ODBC DRIVER I got: C:\informix\bin\iclit09b.dll
For driver: SQL Server I got: C:\WINDOWS\system32\SQLSRV32.dll
RegGetStringDirect() is my function based on Windows API to read something from registry.
EDIT:
Two functions to read "SQL Server" ODBC driver dll name by Ron Schuster moved from comment:
procedure TForm1.Button1Click(Sender: TObject);
//using Windows API calls
var
KeyName, ValueName, Value: string;
Key: HKEY;
ValueSize: Integer;
begin
ValueName := 'Driver';
KeyName := 'SOFTWARE\ODBC\ODBCINST.INI\SQL Native Client';
if RegOpenKeyEx(HKEY_LOCAL_MACHINE, PChar(KeyName), 0, KEY_READ, Key) = 0 then
if RegQueryValueEx(Key, PChar(ValueName), nil, nil, nil, #ValueSize) = 0 then begin
SetLength(Value, ValueSize);
RegQueryValueEx(Key, PChar(ValueName), nil, nil, PByte(Value), #ValueSize);
ShowMessage(Value);
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
//using TRegistry class
var
KeyName, ValueName, Value: string;
Reg: TRegistry;
begin
ValueName := 'Driver';
KeyName := 'SOFTWARE\ODBC\ODBCINST.INI\SQL Native Client';
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_LOCAL_MACHINE;
if Reg.OpenKeyReadOnly(KeyName) then begin
Value := Reg.ReadString(ValueName);
ShowMessage(Value);
end;
finally
Reg.Free;
end;
end;