inno-setuppascalscript

How to download and unzip/extract specific files selected by user in Components List


Hopefully the title is clear enough already, but to explain better: I have a large selection of options in the Select Components page (about 1.4 GB of .png files) and my goal is to lighten the compile size for the installer by having it download files to {tmp} and installed after they've been unzipped. Unfortunately, I've ran into a problem while trying to follow answers from other questions:

All files are downloaded regardless of what components the user selected, leading to large amounts of time spent extracting, which also unfortunately has no progress bar to give feedback to the user.


Currently all [Code] for my installer (sorry, it's lengthy)

[Files]
// Test
Source: "{tmp}\packs\Sapphic_icons\Dead by Daylight\DeadByDaylight\Content\UI\Icons\README.txt"; DestDir: "{app}"; Components: sapphic; Flags: ignoreversion recursesubdirs createallsubdirs external; Check: ExtractedFileNeedsInstallation

[Types]
Name: "sapphic"; Description: "Sapphic Pack";
Name: "custom"; Description: "Customized"; Flags: iscustom;

[Components]  
Name: "sapphic"; Description: "Sapphic Pack - WIP*"; Types: custom sapphic;

[Code]
{ —————————— Extraction Logging ———————————————————————————————————————— }
const
  NO_PROGRESS_BOX = 4;
  RESPOND_YES_TO_ALL = 16;
procedure UnZip(ZipPath, FileName, TargetPath: string); 
var
  Shell: Variant;
  ZipFile: Variant;
  Item: Variant;
  TargetFolder: Variant;
begin
  Shell := CreateOleObject('Shell.Application');

  ZipFile := Shell.NameSpace(ZipPath);
  if VarIsClear(ZipFile) then
    RaiseException(Format('Cannot open ZIP file "%s" or does not exist', [ZipPath]));

  Item := ZipFile.ParseName(FileName);
  if VarIsClear(Item) then
    RaiseException(Format('Cannot find "%s" in "%s" ZIP file', [FileName, ZipPath]));

  TargetFolder := Shell.NameSpace(TargetPath);
  if VarIsClear(TargetFolder) then
    RaiseException(Format('Target path "%s" does not exist', [TargetPath]));

  TargetFolder.CopyHere(Item, NO_PROGRESS_BOX or RESPOND_YES_TO_ALL);
end; // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
{ —————————— Extraction Handling ———————————————————————————————————————— }
function ExtractedFileNeedsInstallation: Boolean;
var 
  TargetPath: String; 
begin  
  TargetPath := ExpandConstant('{userappdata}')+'\App\packs';
  Result := not FileExists(TargetPath);
  Log(Format('ExtractedFileNeedsInstallation: %d', [Result]));  
end; // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
{ —————————— Download Progress ———————————————————————————————————————— }
var
  DownloadPage: TDownloadWizardPage;
function OnDownloadProgress(const Url, FileName: String; const Progress, ProgressMax: Int64): Boolean;
begin
  if Progress = ProgressMax then
    Log(Format('Successfully downloaded file to {tmp}: %s', [FileName]));
  Result := True;
end; // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
{ —————————— Setup Wizard ———————————————————————————————————————— }
procedure InitializeWizard();
begin
{ Download Page }
  DownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), @OnDownloadProgress);
end; // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
{ —————————— Download Handling ———————————————————————————————————————— } 
function NextButtonClick(CurPageID: Integer): Boolean;
var  
  Temp: String;
begin
  if CurPageID = wpReady then begin
      if (not ExtractedFileNeedsInstallation) then
      begin
          Result := True;
      end
      else
      begin
          DownloadPage.Clear;
          DownloadPage.Add('https://www.dropbox.com/scl/fi/fautmpacdsl22h6y41jrv/packs.zip?rlkey=ul3kl1f9o3258yguitprisx6c&st=wwry4esv&dl=1', 'packs.zip', '');
          DownloadPage.Show;
          try
            try
              DownloadPage.Download;
              Temp := ExpandConstant('{tmp}');
              UnZip(Temp+'\packs.zip', 'packs', Temp);
              Result := True;
            except
              SuppressibleMsgBox(AddPeriod(GetExceptionMessage), mbCriticalError, MB_OK, IDOK);
              Result := False;
            end;
          finally
            DownloadPage.Hide;
          end;
      end;
  end else
    Result := True;
end; // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //

Solution

  • Your code includes only single ZIP file download and single component. What does not really correspond to your claims.

    Anyway, if you want to download only subset of files for each of your supposed components, you need to split your downloads to separate component packages in the first place. That's not even Inno Setup question.

    Then you can tell Inno Setup to download only the packages you need, by querying selected components using WizardIsComponentSelected and adding corresponding download URL.

    Like:

    if WizardIsComponentSelected('component1') then 
      DownloadPage.Add('https://example.com/component1.zip', 'component1.zip', '');
    
    if WizardIsComponentSelected('component2') then 
      DownloadPage.Add('https://example.com/component2.zip', 'component2.zip', '');
    
    // ...
    

    Similarly, you then need to UnZip each selected/downloaded package separately:

    if WizardIsComponentSelected('component1') then 
      UnZip(Temp+'\component1.zip', 'packs', Temp);
    
    if WizardIsComponentSelected('component2') then 
      UnZip(Temp+'\component2.zip', 'packs', Temp);
    
    // ...
    

    Though I do not see why you unzip to Temp/{tmp} – From your other questions, I'm suspecting that you then use [Files] to copy from {tmp} to {app} using Flags: external. But you can unzip directly to {app}!


    If you have more components, you will want to factor out the above code to a functions, not to repeat it.

    procedure AddDownloadIfComponentSelected(Comp, BaseName: string);
    begin
      if WizardIsComponentSelected(Comp) then 
        DownloadPage.Add('https://example.com/' + BaseName, BaseName, '');
    end;
    
    procedure UnZipIfComponentSelected(Comp, BaseName, FileName, TargetPath: string);
    begin
      if WizardIsComponentSelected(Comp) then 
        UnZip(ExpandConstant('{tmp}') + '\' + BaseName, FileName, TargetPath);
    end;
    

    Then you use them like:

    AddDownloadIfComponentSelected('component1', 'component1.zip');
    AddDownloadIfComponentSelected('component2', 'component2.zip');
    
    UnZipIfComponentSelected('component1', 'component1.zip', 'packs', Temp);
    UnZipIfComponentSelected('component2', 'component2.zip', 'packs', Temp);
    

    If you always use 'packs' and Temp, you can of course hard-code them in UnZipIfComponentSelected and you do not have to repeat them in every call.


    If you have a LOT of components, you might want to align their and packages' names to be able to automate the download selection – not to have to repeat the above code (not even the function call) for each and every.

    Use WizardSelectedComponents to retrieve a list of selected components. Like this (untested):

    var
      Components: TStringList;
      Component: string;
      I: Integer;
    begin
      Components := TStringList.Create();
      Components.CommaText := WizardSelectedComponents(False);
      for I := 0 to Components.Count - 1 do
      begin
        Component := Components[I];
        if WizardIsComponentSelected(Component) then 
          DownloadPage.Add(
            'https://example.com/' + Component + '.zip', Component + '.zip', '');
      end;
    end;
    

    And similarly for unzipping:

    for I := 0 to Components.Count - 1 do
    begin
      Component := Components[I];
      if WizardIsComponentSelected(Component) then 
        UnZip(Temp + '\' + Component + '.zip', 'packs', Temp);
    end;