Learn what Felgo offers to help your business succeed. Start your free evaluation today! Felgo for Your Business

DownloadableResource

The DownloadableResource item allows to download additional assets from the Internet. More...

Import Statement: import Felgo 4.0
Since: Felgo 2.17.0

Properties

Signals

Methods

Detailed Description

DownloadableResource allows downloading app and game assets on demand during runtime. That way not all resources must be included in the app binary and therefore result in smaller downloads from the app stores.

The most popular use cases for downloadable packages are:

  • You want to keep your app store binary as small as possible for the first download, to increase the download numbers of your app or game with a smaller download size.
  • You want to download additional content packages after in-app purchases.
  • Keep your initial app size below the store limits:
    • On Google Play, your initial apk size must be below 100MB, after that you need to use Android expansion files. To avoid that, you can just use DownloadableResource and download the additional files at a later time.
    • On iOS, your initial binary size limit is 150MB for mobile network downloads. If your binary is bigger, the user can only download your app over WiFi. Downloading additional resources later also helps you to avoid this limit.
    • With the Felgo DownloadableResource component, you can create a cross-platform solution to work with downloadable resources, that works for both iOS AND Android. It even works on Desktop too, with a single source code for all platforms! This way, you do not need to deal with Android expansion files and can create a working solution for all platforms instead.

DownloadableResource can load files from any HTTP(S) web addresses. You can add a secret to protect and restricts downloads to your app or game only. You can download single files or entire .zip-archives, which are automatically extracted for further usage.

The item downloads and extracts to a standard path or custom location. You can then use downloaded resources from within your app or game as you do with local resources.

Supported Resource Types

The DownloadableResource item currently supports all kinds of files that you use with Felgo components. Examples are:

Assets downloaded with DownloadableResource are persisted between app starts, so they are cached for offline usage as well.

Storage Location

You can set the path where to download and extract the files to with the storageLocation property. Depending on your usecase, you can choose one of the available FileUtils standard paths. You can alternatively set the full path with the storagePath property.

The default value of storageLocation is FileUtils.AppDataLocation, which is a default location for your app's private files that are then available for as long as the app is installed on a device.

To set the file name of the resulting file, use the storageName property.

The following table lists the platform-specific paths for FileUtils.AppDataLocation:

Platform Path
Android "<APPROOT>/files"
iOS "<APPROOT>/Library/Application Support"
Linux "~/.local/share/<APPNAME>"
macOS "~/Library/Application Support/<APPNAME>"
Windows "C:/Users/<USER>/AppData/Roaming/<APPNAME>"

How to Create Downloadable Packages

To download multiple files at once, create an archive files with .zip file ending. You can use any data compression application that supports the zip archive file type.

The item supports subfolders within the zip archive to structure downloadable resources.

Example Usage

To just download a single file from a web server, use the following example code:

 import Felgo

 App {
   DownloadableResource {
     id: resource1

     extractAsPackage: false // false for single files
     source: "https://felgo.com/web-assets/girl.jpg"
   }
 }

Likewise, you can download and extract a .zip package like the following (note the extractAsPackage):

 import Felgo

 App {
   DownloadableResource {
     id: resource1

     extractAsPackage: true // true for zip archives
     source: "https://felgo.com/web-assets/girl.zip"
   }
 }

To start the actual download, call the download() method.

To check the status of a package, use the status property. The convenience property available is true after the status changes to DownloadableResource.Available.

If you download single files (i.e. extractAsPackage is false), storagePath contains the full URL to the downloaded file.

Placeholder Image and "Available" Property

Use the available property to display a placeholder image while downloading:

 AppImage {
   source: resource1.available ? resource1.storagePath : "local-image.png"
 }

If you extract packages, you can retrieve the path to an extracted file using getExtractedFileUrl(). The parameter is a relative path to a file within a downloaded and extracted .zip package. This returns the full URL to the file which you can use anywhere within your app or game (i.e. the source property of an AppImage).

In the following example, extracted-image.png is a file within the downloaded .zip archive. local-image.png is a local file at the same path as the QML file.

 AppImage {
   source: resource1.available ? resource1.getExtractedFileUrl("extracted-image.png") : "local-image.png"
 }

You can also use sub-folders within extracted .zip archives. getExtractedFileUrl() supports relative paths to nested files:

 AppImage {
   source: resource1.available ? resource1.getExtractedFileUrl("content/images/extracted-image.png") : "local-image.png"
 }

Additionally you can split up resources over multiple download packages. The extraction path of each package is registered with FileUtils using FileUtils::addSearchPath(). This allows FileUtils::getMultiPathUrl() to find files from any downloaded package. For example, if you have a file called "image.png" that is part of one of multiple download packages, you can display the image like the following:

 AppImage {
   source: FileUtils.getMultiPathUrl("image.png")
 }

If the file is not found in any extracted package, a fallback file with the same name can be loaded from other search paths.

Note: The search order for files with the same filename is not defined. To use FileUtils::getMultiPathUrl(), keep the filename unique across all downloads. Also make sure to avoid resources with the same relative file path in different extracted packages.

Open downloaded files

You can open files you have downloaded with an external application using FileUtils::openFile(). If you download single files, you can directly use storagePath with this method.

The following example downloads a PDF file and then opens it with the default installed PDF viewer:

 import Felgo
 import QtQuick

 App {
   id: app

   DownloadableResource {
     source: "http://www.orimi.com/pdf-test.pdf"
     storageLocation: FileUtils.DocumentsLocation
     storageName: "pdf-test.pdf"
     extractAsPackage: false

     Component.onCompleted: {
       if(status === DownloadableResource.UnAvailable) {
         download()
       } else {
         openDownloadedFile()
       }
     }

     onStatusChanged: openDownloadedFile()

     function openDownloadedFile() {
       if(status === DownloadableResource.Available) {
         FileUtils.openFile(storagePath)
       }
     }
   }
 }

You can also open files extracted from .zip packages. For this, you can use getExtractedFileUrl() to open extracted files like in the following example:

 FileUtils.openFile(resource1.getExtractedFileUrl("extracted-file.pdf"))

Using a Secret to Protect Your Downloads

Sometimes you want to protect your resources from downloads outside your app. For this usecase the DownloadableResource item provides the secret property. If the secret property is set, the item appends HTTP GET parameters to your provided source. This looks like the following:

 https://example.com/resource1.zip?token=3fcdb553d1abd6e57d5d4496c5f86eb690fbd319&timestamp=1524706331

The token consists of the following checksum:

 shasum( appid + ":" + secret + ":" + timestamp )
  • The appid is the identifier given in your config.json file.
  • The secret is this property.
  • The timestamp is the current timestamp in UTC time zone in seconds since January 1, 1970.

Your server-side implementation should check the token against the following criteria:

  • The age of the timestamp, for example deny the request if it is older than 5 minutes.
  • Match the given token with a token generated on server side. Use the given timestamp, stored secret and known app identifier.

If the token meets all criteria, you can return the requested zip file. Otherwise return a HTTP 401 status code.

Note: Use the source property with HTTPS protocol when using secured package functionality.

Notes on Multi-Threading

The download and extraction process is performed asynchronously in the background.

Downloading and extracting large files may take a while depending on the file size. It is good practice to display a progress bar during the download. You can do so with the progress property. You can check individual progress with the downloadProgressChanged and extractionProgressChanged signals.

It is thread-save to call download() and then listen to the available property. See more in Example Usage.

Advanced Usage

DownloadableResource offers advanced properties, functions and signals. Use these to control the download and extraction process.

You can set a standard location for download with the storageLocation property. Use one of the FileUtils storage location constants. Choose a name a name for the downloaded or extracted file or folder with storageName. To set the complete path manually, use the storagePath property. This overrides both storageLocation and storageName.

You can supply additional HTTP headers using headerParameters.

Use status and progress to query the current status of the download and extraction progress. You can cancel an ongoing operation using cancel().

This QML example app uses all the available features of DownloadableResource:

 import Felgo
 import QtQuick
 import QtQuick.Controls

 App {
   id: app

   //all enum values to be able to select in a dialog
   readonly property var allStandardPaths: [
     FileUtils.DocumentsLocation,
     FileUtils.DownloadLocation,
     FileUtils.CacheLocation,
     FileUtils.DesktopLocation,
     FileUtils.FontsLocation,
     FileUtils.ApplicationsLocation,
     FileUtils.MusicLocation,
     FileUtils.MoviesLocation,
     FileUtils.PicturesLocation,
     FileUtils.TempLocation,
     FileUtils.HomeLocation,
     FileUtils.DataLocation,
     FileUtils.GenericDataLocation,
     FileUtils.RuntimeLocation,
     FileUtils.ConfigLocation,
     FileUtils.GenericCacheLocation,
     FileUtils.GenericConfigLocation,
     FileUtils.AppDataLocation,
     FileUtils.AppConfigLocation,
     FileUtils.AppLocalDataLocation
   ]

   DownloadableResource {
     id: dl

     //shorthand property if status is busy
     property bool busy: status === DownloadableResource.Downloading || status === DownloadableResource.Extracting

     //download and extract .zip
     source: "https://www.dropbox.com/s/z2dxyn2847stnq3/multiResolutionImage.zip?dl=1"

     //set storage folder from standard path
     storageLocation: FileUtils.DownloadLocation

     //set file/folder name for storage
     storageName: "multiResolutionImage"

     //additional HTTP headers
     headerParameters: ({
                          "X-Custom-Header": "header-value1",
                          "X-Custom-Header2": "header-value2"
                        })

     //show progress in bar
     onProgressChanged: progressBar.value = progress
   }

   AppPage {
     anchors.fill: parent
     anchors.topMargin: Theme.statusBarHeight

     ProgressBar {
       id: progressBar
       anchors.left: parent.left
       anchors.right: parent.right
       anchors.top: parent.top
       height: dp(32)

       minimumValue: 0
       maximumValue: 100
     }

     AppListView {
       anchors.fill: parent
       anchors.topMargin: progressBar.height

       //model defines the UI items
       model: [
         {
           text: "Available: " + dl.available,
           enabled: false
         }, {
           text: "Status: " + dl.statusText,
           enabled: false
         }, {
           text: "Source:",
           detailText: dl.source,
           action: "source"
         }, {
           text: "Extract as package?",
           action: "extract",
           checked: dl.extractAsPackage
         }, {
           text: "Storage location: " + dl.storageLocationDisplayName,
           action: "storageLocation",
           enabled: !dl.busy
         }, {
           text: "Storage name: " + dl.storageName,
           action: "storageName",
           enabled: !dl.busy
         }, {
           text: "Storage path:",
           detailText: dl.storagePath,
           action: "storagePath",
           enabled: !dl.busy
         }, {
           text: "Remove",
           action: "remove",
           enabled: dl.available
         }, {
           text: "Start download",
           action: "download",
           enabled: !dl.busy && !dl.available
         }, {
           text: "Cancel",
           action: "cancel",
           enabled: dl.busy
         }, {
           text: "Full URL of image in package:",
           detailText: dl.available ? dl.getExtractedFileUrl("+hd/testContentScaling.png") : "(unavailable)",
           enabled: dl.available,
           action: "showImage"
         }

       ]

       //delegate defines the list item + interaction
       delegate: SimpleRow {
         style.backgroundColor: enabled ? "white" : "#ddd"

         iconSource: modelData.action === "extract" ? IconType.check : ""
         icon.color: modelData.checked ? "black" : "#40000000"

         onSelected: {
           switch(modelData.action) {
           case "extract":
             dl.extractAsPackage = !dl.extractAsPackage
             break;
           case "remove":
             dl.remove()
             break;
           case "download":
             dl.download()
             break;
           case "cancel":
             if(dl.busy) {
               dl.cancel()
             }
             break;
           case "source":
             InputDialog.inputTextSingleLine(app, "Enter URL:", "https://", function(accepted, text) {
               if(accepted) dl.source = text
             }, true)
             break;
           case "storagePath":
             InputDialog.inputTextSingleLine(app, "Enter file path:", "/", function(accepted, text) {
               if(accepted) dl.storagePath = text
             }, true)
             break;
           case "storageName":
             InputDialog.inputTextSingleLine(app, "Enter file name:", "", function(accepted, text) {
               if(accepted) dl.storageName = text
             }, true)
             break;
           case "storageLocation":
             selectDialog.open()
             break;
           case "showImage":
             var url = dl.getExtractedFileUrl("+hd/testContentScaling.png")
             if(url) {
               dialogImage.source = url
               imgDialog.open()
             }
             break;
           }
         }
       }
     }
   }

   // dialog to select one of the standard path enum values
   Dialog {
     id: selectDialog
     contentHeight: itemList.height
     title: "Select storage location"

     positiveAction: false
     onCanceled: close()

     AppListView {
       id: itemList

       width: parent.width
       height: dp(48) * 6 // max. 6 items

       model: allStandardPaths.map(FileUtils.storageLocationDisplayName)
       delegate: SimpleRow {
         text: modelData

         onSelected: {
           dl.storageLocation = allStandardPaths[index];
           selectDialog.close();
         }
       }
     }
   }

   // dialog to show an image from inside the downloaded package
   Dialog {
     id: imgDialog
     contentHeight: dialogImage.height
     title: "Image"

     positiveAction: false
     onCanceled: close()

     AppImage {
       id: dialogImage

       width: parent.width
       height: dp(120)
       fillMode: Image.PreserveAspectFit
     }
   }
 }

See also FileUtils.

Property Documentation

available : bool

A read-only property indicating if the specific resource is ready to use, i.e. downloaded and extracted.

You can use this property like in the following example:

 AppImage {
   source: resource1.available ? dl.getExtractedFileUrl("package-image.png") : "local-image.png"
 }

See also status.


extractAsPackage : bool

This property indicates if the file to download is a .zip package archive and needs to be extracted after download.

Set this property to true to open and extract the .zip package after download. Set this property to false to download a single file.

The default value of this property is true.


headerParameters : var

You can use this property to add additional HTTP request header fields for the download.

Set a JavaScript object containing the additional headers for the HTTP request. Each key/value pair in the object will be used as a header. Use strings as keys and values, like in this example:

 DownloadableResource {
   source: "https://example.com/package1.zip"

   headerParameters: ({
     "Accept-Language": "en-us",
     "Authorization": "Basic abcdefghi",
   })
 }

Note: Make sure to add an extra pair of parenthesis around the header map object: headerParameters: ({...}) to ensure the object is not interpreted as a code block.


progress : int

This property indicates the progress of an ongoing download or extraction.

The download and extraction of large files may take some time. Use this property to report the progress with the help of a progress bar or text output.

If the status is DownloadableResource.Downloading or DownloadableResource.Extracting, the property is set to the download or extraction status in percentage between 0 and 100.

If the status is DownloadableResource.UnAvailable, the property is set to 0.

If the status is DownloadableResource.Available, the property is set to 100.

See also downloadProgressChanged() and extractionProgressChanged().


secret : string

You can use this property to protect the file download from access outside of your app or game.

If set, the item appends a generated token to the given source property. For more information see Using a Secret to Protect Your Downloads.


source : url

This property holds the source URL with either HTTP or HTTPS scheme of the downloadable file.

See How to Create Downloadable Packages for more information about package creation.


status : enumeration

A read-only property indicating the current status of the download. Possible values are:

  • DownloadableResource.UnAvailable - The package is not available or was cleared from temporary locations, call download() to start the download (again).
  • DownloadableResource.Available - The package was successfully downloaded and optionally extracted and is ready to use.
  • DownloadableResource.Downloading - The package is currently downloading. You can check the download status with downloadProgressChanged() and progress.
  • DownloadableResource.Extracting - The content of the package is currently extracting. You can check the extraction status with extractionProgressChanged() and progress.

See also available and statusText.


statusText : string

A read-only property indicating the current status of the download as a readable string. Use this property to display the current status to the user.

Possible values are UnAvailable, Available, Downloading and Extracting.

See also status.


storageLocation : enumeration

This property determines the storage location for the downloaded and/or extracted files. It sets the storagePath based on one of the following FileUtils constants:

  • FileUtils.DesktopLocation
  • FileUtils.DocumentsLocation
  • FileUtils.FontsLocation
  • FileUtils.ApplicationsLocation
  • FileUtils.MusicLocation
  • FileUtils.MoviesLocation
  • FileUtils.PicturesLocation
  • FileUtils.TempLocation
  • FileUtils.HomeLocation
  • FileUtils.DataLocation
  • FileUtils.CacheLocation
  • FileUtils.GenericDataLocation
  • FileUtils.RuntimeLocation
  • FileUtils.ConfigLocation
  • FileUtils.DownloadLocation
  • FileUtils.GenericCacheLocation
  • FileUtils.GenericConfigLocation
  • FileUtils.AppDataLocation
  • FileUtils.AppConfigLocation
  • FileUtils.AppLocalDataLocation

The default value is FileUtils.AppDataLocation, which is a default location for your app's private files that are then available for as long as the app is installed on a device.

The following table lists the platform-specific paths for FileUtils.AppDataLocation:

Platform Path
Android "<APPROOT>/files"
iOS "<APPROOT>/Library/Application Support"
Linux "~/.local/share/<APPNAME>"
macOS "~/Library/Application Support/<APPNAME>"
Windows "C:/Users/<USER>/AppData/Roaming/<APPNAME>"

If you use a temporary location, like FileUtils.CacheLocation, the system might delete files automatically to regain storage space. To prevent side-effects when using such locations, use the available property to check if downloaded and/or extracted files still exist or need to be downloaded again.

The full storagePath gets set by appending the storageName to the path of the storageLocation.

For example, you can extract an archive to <downloads directory>/myArchiveContents: Set storageLocation to FileUtils.DownloadLocation and storageName to "myArchiveContents".

Use a unique combination of storageLocation and storageName to prevent overwriting files.

Use storagePath to get the resulting path where files get downloaded and extracted. It contains the full path after assigning storageLocation and storageName.

See also storageName and storagePath.


storageLocationDisplayName : string

This read-only property contains a textual representation of the storageLocation property.


storageName : string

This property sets the file or folder name for the storageLocation if set.

The full storagePath is set by appending the storageName to the path of the storageLocation.

For example, you can extract an archive to <downloads directory>/myArchiveContents: Set storageLocation to FileUtils.DownloadLocation and storageName to "myArchiveContents".

Note: make sure to use a unique combination of storageLocation and storageName to prevent overwriting files.

If not set, the item uses a name based on the source property.

Use storagePath to get the resulting path where files get downloaded and extracted. It contains the full path after assigning storageLocation and storageName.

See also storageLocation and storagePath.


storagePath : url

This property is used as the storage base path of your downloaded resources. This overrides both the storageName and storageLocation properties. If used, set this property to a full path where you want to download and extract the file.

This property allows to check the resulting path for the specific files.

If not set, it ist set to a default path based based on the storageName and storageLocation properties. This is useful to check the downloaded and/or extracted file contents on Desktop platforms during development. Keep in mind that access to these directories may be limited on mobile platforms due to sandboxing and file system design.

See also storageName, storageLocation, and getExtractedFileUrl().


Signal Documentation

downloadFinished(error)

This signal is emitted as soon as the download and extraction has finished.

The error parameter indicates the result as one of the following values:

  • DownloadableResource.NoError - The download and extraction finished successfully and the files are ready to use.
  • DownloadableResource.NetworkError - The download couldn't be finished because of a network error. This usually means that the device has no Internet connection.
  • DownloadableResource.UnauthorizedError - The web service at the given source returned a 401/403 error. This usually means that the provided secret is invalid.
  • DownloadableResource.FilePathError - The files can't be saved under the given path.
  • DownloadableResource.FileExtractionError - The package can't be extracted. This usually means the downloaded file is no valid zip archive file. It can also mean the extraction was canceled with cancel().
  • DownloadableResource.DownloadCancelledError - The user canceled the download with cancel().
  • DownloadableResource.UnknownError - There occurred an unspecified error while downloading or extracting.

Note: The corresponding handler is onDownloadFinished.


downloadProgressChanged(progress)

This signal is emitted when the download status of the package changes.

The download of large files may take some time. Use this property to report the progress with the help of a progress bar or text output.

The progress parameter indicates the download status in percentage between 0 and 100. You can also check the progress with the property progress.

Note: The corresponding handler is onDownloadProgressChanged.

See also progress and extractionProgressChanged().


downloadStarted()

This signal is emitted as soon as the download of the packages started after calling download().

Note: The corresponding handler is onDownloadStarted.


extractionProgressChanged(progress)

This signal is emitted when the extraction status of the downloaded archive changes.

The extraction of large files may take some time. Use this property to report the progress with the help of a progress bar or text output.

The progress parameter indicates the extraction status in percentage between 0 and 100. You can also check the progress with the property progress.

Note: The corresponding handler is onExtractionProgressChanged.

See also progress and downloadProgressChanged().


Method Documentation

void cancel()

Call this method to cancel a currently running download or extraction process. This cleans up already downloaded and/or extracted files. After this call, status is DownloadableResource.UnAvailable.

If status is anything other than DownloadableResource.Downloading or DownloadableResource.Extracting, calls to that method are ignored.

See also download().


void download()

Starts the download from the given source. You can check the status of the download with the downloadProgressChanged() signal.

A download only starts if status is DownloadableResource.UnAvailable, otherwise calls to that method are ignored.

See also cancel().


url getExtractedFileUrl(string relativePath)

Returns a full URL to a file inside a downloaded and extracted package.

This function is only required for files from archives with extractAsPackage set to true. For all other files without extraction, you can just use storagePath.

The return value is a file:// URL to the storagePath with appended relativePath.

See also storagePath.


void remove()

Call this method to remove all previously downloaded and extracted files.

After this call, status is DownloadableResource.UnAvailable. If status is anything other than DownloadableResource.Available, calls to that method are ignored.


Qt_Technology_Partner_RGB_475 Qt_Service_Partner_RGB_475_padded