How to download stuff now that transfer-file is deprecated?

I asked my professor, he doesn’t know. I asked here in the forums in various threads, i’ve mostly been ignored. I created a new thread in the Ionic subreddit over reddit.com and still nothing.

I just want to know how to download stuff now that File-Transfer is deprecated. In my case i simply want to download image files from direct URLs.

Please help.

@Fieel
I know that getting a solution is hard nowadays. Peoples only answer the questions they know or they backoff by thinking who’ll invest time on this, it’s not my problem.
So I’ll suggest to do some R&D yourself.

Cheers!!

2 Likes

I’ll definetelly post my solution as soon as i find one, i’m doing a lot of R&D myself, i don’t mind that. It’s been almost a week now though so i’m not sure i can find the solution by myself, i’ll keep trying though and post back in case.

2 Likes

I hope you find out the solution and come up more strong.

1 Like

Thank you. I’m also a beginner with Ionic and Angular in general so i don’t blame anyone ahah.

Basically, from what i understood from this blog post found in the Cordova’s website blog, file-transfer shouldn’t be used anymore but we should instead use the new XMLHttpRequest (or XHR).

Unfortunately i can’t find any examples on how to implement this in an existing Ionic project, probably because i’m too noob to understand, if someone has an example to showcase how to use XHR to download any file i’d greatly appreciate it!

Actually I’m still using the file transfer plugin and it is working fine yet.

look here

Angular’s httpclient is an inplementation of XHR. But maybe use the plain vanilla example first as per @avishai_peretz_dev recommendation

UPDATE — XMLHTTP variant - It works but better solution below this post

This is what i came up to - please guys let me know if i can optimize stuff or if the approach i found is just plain stupid!

I used plain XMLHttpRequest, i couldn’t get it to work with blobs using Ionic’s http provider! :frowning: This is the documentation i used to learn to use this kind of construct: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest

    downloadImage(url, name, file?, alertCtrl?) {
        file = this.file;
        alertCtrl = this.alertCtrl;

        let alert = alertCtrl.create({
            title: 'Download starting...',
            subTitle: name+" will be downloaded.",
            buttons: ['Cool, thanks!']
        });
        alert.present();


        //REQUEST CREATION
        let oReq = new XMLHttpRequest();

        //SENDING REQUEST
        oReq.open("GET", url, true);
        oReq.responseType = "blob"; // blob pls

        //IF DATA RECEIVED THEN  WRITE FILE
        oReq.onload = function(oEvent) {

            //SAVE TEMP FILE IN APP FOLDER
            file.writeFile(file.externalDataDirectory, 'tmp.jpg', oReq.response, { replace: true }).then( data => {

                //COPY TMP FILE INTO USER GALLERY
(<any>window).cordova.plugins.imagesaver.saveImageToGallery(file.externalDataDirectory+'tmp.jpg', onSaveImageSuccess, onSaveImageError);

                //ON SUCCESS
                function onSaveImageSuccess(){

                    let alert = alertCtrl.create({
                        title: 'Image saved!',
                        subTitle: 'The picture \"'+name+'\" has been saved in your phone gallery.',
                        buttons: ['Cool, thanks!']
                    });
                    alert.present();

                }

                //ON FAIL
                function onSaveImageError(error) {

                    let alert = this.alertCtrl.create({
                        title: 'There was a problem downloading your image :(.',
                        subTitle: 'For some reason we couldn\'t save the picture in your gallery. Please try again!',
                        buttons: ['Damn!']
                    });
                    alert.present();
                }
            });
        };
        oReq.send();//this is useless right?
    }

It works. The downloaded picture is moved into the gallery exactly like any downloaded image from your browser.

Now i’m trying to add tracking for the download because as of now the user has no clue if the download is actually working apart from the alerts at start and finish. From the doc page i linked early looks like i can use this:

// progress on transfers from the server to the client (downloads)
function updateProgress (oEvent) {
  if (oEvent.lengthComputable) {
    var percentComplete = oEvent.loaded / oEvent.total * 100;
    // ...
  } else {
    // Unable to compute progress information since the total size is unknown
  }
}

I’ll try to implement something similar but if you guys have some examples i’d gladly appreciate it! Thank you for your help guys, love ya :smiley:

I was struggling with this a couple of days ago, my specific case was to download an image, but I guess the approach is the same for other files. This is a small snippet of my code. I am using Ionic 3.9.2, Angular 5.2.9 and the new HttpClient:

// The code asumes you have the native File plugin injected and the instance is called "file"
downloadAndSavePicture(pictureUrl) {
    this.http.get(pictureUrl, {responseType: 'blob'})
    .subscribe((imageBlob: Blob) => {
        // imageBlob is the binary data of the the image
        // From here you can manipulate it and store it where you want
        // For example, to store it in your app dir
        // The replace true is optional but is just in case you want to overwrite it
        return this.file.writeFile(this.file.dataDirectory, "my_downloaded_image", imageBlob, {replace: true});
    }).toPromise();
}

As you can see it is pretty straighforward, the trick here is to set the response type to blob. Also if you have a big file and you want to get report on the download progress, you can configure the request so it inspects not the body but the request (by passing the observe: 'response' option in the same object as the response type).
I hope this helps

Edit:

The toPromise() at the end is to make sure you know when the file has been written and that way you can execute other operations.

3 Likes

Thank you, that’s exactly what i was looking for. I expanded the code to also support progress tracking so i could implement a progress bar:

downloadImage(pictureUrl){

        //new request
        const req = new HttpRequest('GET', pictureUrl, {
            reportProgress: true,
            responseType: "blob"//blob type pls
        });

        //all possible events
        this.http.request(req).subscribe((event: HttpEvent<any>) => {
            switch (event.type) {
                case HttpEventType.Sent:
                    console.log('Request sent!');
                    break;
                case HttpEventType.ResponseHeader:
                    console.log('Response header received!');
                    break;
                case HttpEventType.DownloadProgress:
                    //i use a gloal progress variable to save progress in percent
                    this.progress = Math.trunc(event.loaded / event.total * 100);
                    break;
                case HttpEventType.Response:
                    //do whatever you have to do with the file using event.body
                    console.log('😺 Done!', event.body);
                    //i'm gonna write the file in my case
                    this.file.writeFile(this.file.dataDirectory, "tmp.jpg", event.body, {replace: true}).then( (file) =>{
                    
                             //using a cordova plugin to move my file from a tmp folder to the user image folder
                            //https://github.com/quiply/SaveImage
             (<any>window).cordova.plugins.imagesaver.saveImageToGallery(this.file.dataDirectory+'tmp.jpg', onSaveImageSuccess, onSaveImageError);

                            function onSaveImageSuccess(){
                            //in case of imagesave success
                            }
                            function onSaveImageError(error) {
                            //in case of imagesaver fail
                            }
                     }).catch(
                     (err) => {
                         //in case of file.writefile fail
                     });
            }
        });
    }

And to display progress in a nice progress bar i followed this easy tutorial in which you create a custom component that uses my progress global variable here to auto-update:

        <progress-bar [progress]="this.progress"></progress-bar>

1 Like

@cadavid219:

Don’t use a subscribe like that. The problem is that you won’t know when or if the writefile actually completed. This is a lot better, because it gives you an ack.

downloadAndSavePicture(pictureURL): Promise<see below> {
  return this.http.get<Blob>(pictureURL)
                  .pipe(switchMap(blob => this.file.writeFile(same as before))
                  .toPromise();
}

The type of the Promise (the “see below” part) will depend on how you set up the writeFile. My recommendation is that you set it up so it returns an Observable which emits TRUE if the write went well, and FALSE otherwise.

2 Likes

Thanks, you are right. I will update my post.

@cadavid219 Without my approach how am i able to track progress?

You can add the tap operator to the Observable, and have it emit information “on the side.”

Super helpful example, thank you! I just used it to migrate successfully.

I tweaked your example to return an Observable so I can respond from wherever I call the download. This version will emit the FileEntry and then complete on success.

downloadFile(url:string, fileName: string): Observable<any> {
  return this.http.get(url, {responseType: 'blob'})
    .flatMap((data: Blob) => {
      return Observable.from(this.file.writeFile(this.file.dataDirectory, fileName, data, {replace: true}))
    })
}

//to call it
this.downloadFile(myUrl, myFileName)
.subscribe(x => {
  //handle it
});
1 Like

Nice helpful example! Hope this thread help whoever is migrating from the old transfer-file.

This is such an incredibly productive thread !
I just encountered the problem, read this, found my solution, thanks !
You all saved me so much time

3 Likes

I am trying to download a file, tried your example, I don’t know how you guys got it working? I am getting DOMException, see screenshot below.


Then I tried creating a directory on Android device and I get some what similar, but called Security error.

 downloadFile(sessionId: string, type: string): Observable<any> {
        const something = this.file.dataDirectory;
        console.log(something);
        this.file.createDir(something, 'mydir10', true);
        
        return this.http.get(`${this.baseApi}/data-export?sessionId=${sessionId}&fileType=${type}`, {responseType: 'blob'})
          .flatMap((data: Blob) => {
            return Observable.fromPromise(this.file.writeFile(something, `view.${type}`, data, {replace: true}));
          });
    }

Any ideas how do I go about fixing this?

Hi Fieel,
This looks good, but I still have an issue.
Where did you get the “http” in “this.http.request(req).subscribe…” from? The angular Http is giving the following error for the HttpRequest object (req)

“Argument of type ‘HttpRequest<{}>’ is not assignable to parameter of type ‘string | Request’.”

The ionic HTTP is completely off to use here, I guess.

Edit:
I think I have the HttpClient configured okay, but I keep getting the following error:

“headers”: {
“normalizedNames”: {},
“lazyUpdate”: null,
“headers”: {}
},
“status”: 0,
“statusText”: “Unknown Error”,
“url”: null,
“ok”: false,
“name”: “HttpErrorResponse”,
“message”: “Http failure response for (unknown url): 0 Unknown Error”,
“error”: {
“isTrusted”: true
}

I saw somewhere that it is a CORS related error, but i haven’t the faintest idea how to go about resolving it. I am testing on simulator. The following is the code to download a file at “the_url”:

let headers = new HttpHeaders();
headers.append(‘Access-Control-Allow-Origin’, ‘*’); //http://localhost:8100
headers.append(‘Content-Type’, ‘text/plain charset=UTF-8’);
//new request
const req = new HttpRequest(‘GET’, the_url, {type: ‘text’}, {
headers: headers,
reportProgress: true,
responseType: “text”//blob type pls
});
//all possible events
this.httpxc.request(req).subscribe((event: HttpEvent) => {})