Leonid Zadorozhnykh
Leonid Zadorozhnykh

Reputation: 333

How to download multiple files from Google Drive as .zip using JSZip on Salesforce

The case: On Salesforce platform I use Google Drive to store files (images for this case) with configured Apex Google Drive API Framework. So Google Drive API handles authToken and so on. I can upload and browse images in my application. In my case I want to select multiple files and download them in a single zip file. So far I'm trying to do that using JSZip and FileSaver libraries. With the same code below I can zip and download multiple files stored somewhere else with proper response header, but not from GDrive because of CORS error.

https://xxx.salesforce.com/contenthub/download/XXXXXXXXXX%3Afile%XXXXXX_XXXXXXXXX. No'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://xxx.visual.force.com' is therefore not allowed access. If I just click on this link, file starts to download.

Is there any way to configure GDrive to enable response header: Access-Control-Allow-Origin: * or Access-Control-Allow-Origin: https://*/mydomain.com somehow or I just have to use something else, maybe server side compression? Now I am using the download link provided by Apex Google Drive API (looks like this: https://xxx.salesforce.com/contenthub/download/XXXXXXXXXXX%3Afile%XXXXXXXX), it works fine when used as src="fileURL" or when pasted directly to the browser. GDrive connector add 'accesToken' and so on.

My code:

//ajax request to get files using JSZipUtils
let urlToPromise = (url) =>{
    return new Promise(function(resolve, reject) {
        JSZipUtils.getBinaryContent(url, function (err, data) {
            if(err) {
            reject(err);
            } else {
            resolve(data);
            }
        });
    });
};

this.downloadAssets = () => {
let zip = new JSZip();
//here 'selectedAssets' array of objects each of them has 'assetFiles'
//with fileURL where I have url. Download and add them to 'zip' one by one
for (var a of this.selectedAssets){
    for (let f of a.assetFiles){
        let url = f.fileURL;                
        let name = a.assetName + "." + f.fileType;
        let filename = name.replace(/ /g, ""); 
        zip.file(filename, urlToPromise(url), {binary:true});                                    
    }
}
//generate zip and download using 'FileSaver.js'    
zip.generateAsync({type:"blob"})
    .then(function callback(blob) {
     saveAs(blob, "test.zip");
    });       
};

I also tried to change let url = f.fileURL to let url = f.fileURL + '?alt=media'; and &access_token=CURRENT_TOKEN added by GDrive connector.

this link handled by GRDrive connector so if I just enter it in browser it download the image. However, for multiple download using JS I got CORS error.

Upvotes: 1

Views: 1533

Answers (3)

Leonid Zadorozhnykh
Leonid Zadorozhnykh

Reputation: 333

My previously published code works. Forgot to post a solution. Just instead of using content hub link I started to use direct link to Google Drive and CORS issue was solved. Still not sure if CORS might be solved somehow at Salesforce side. Tried different setups with no luck. Direct download link to GDrive works ok in my case. The only thing I had to change is the prefix to GDrive file ID.

Upvotes: 0

Srusti Thakkar
Srusti Thakkar

Reputation: 1901

According to me, just download all files and store them at temporary directory location and then add that directory to zip file and store that zip to physical device.

 public Entity.Result<Entity.GoogleDrive> DownloadMultipleFile(string[] fileidList)
    {
        var result = new Entity.Result<Entity.GoogleDrive>();
        ZipFile zip = new ZipFile();

        try
        {
            var service = new DriveService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "Download File",
            });

            FilesResource.ListRequest listRequest = service.Files.List();
            //listRequest.PageSize = 10;
            listRequest.Fields = "nextPageToken, files(id, name, mimeType, fullFileExtension)";

            IList<File> files = listRequest.Execute().Files;

            if (files != null && files.Count > 0)
            {
                foreach (var fileid in fileidList)
                {
                    foreach (var file in files)
                    {
                        if (file.Id == fileid)
                        {                                
                                result.Data = new Entity.GoogleDrive { FileId = fileid };
                                FilesResource.GetRequest request = service.Files.Get(fileid);
                                request.ExecuteAsync();
                                var stream = new System.IO.FileStream(HttpContext.Current.Server.MapPath(@"~\TempFiles") + "\\" + file.Name, System.IO.FileMode.Create, System.IO.FileAccess.Write);
                                request.MediaDownloader.ProgressChanged += (IDownloadProgress progress) =>
                                {
                                    switch (progress.Status)
                                    {
                                        case DownloadStatus.Downloading:
                                            {
                                                break;
                                            }
                                        case DownloadStatus.Completed:
                                            {
                                                break;
                                            }
                                        case DownloadStatus.Failed:
                                            {
                                                break;
                                            }
                                    }
                                };
                                request.Download(stream);
                                stream.Close();
                                break;
                            }                                
                        }   
                    }

            }
            zip.AddDirectory(HttpContext.Current.Server.MapPath(@"~\TempFiles"), "GoogleDrive");
            string pathUser = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
            string pathDownload = System.IO.Path.Combine(pathUser, "Downloads");
            zip.Save(pathDownload + "\\GoogleDrive.zip");
            System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(HttpContext.Current.Server.MapPath(@"~\TempFiles"));
            foreach (var file in di.GetFiles())
            {
                file.Delete();
            }

            result.IsSucceed = true;
            result.Message = "File downloaded suceessfully";
        }
        catch (Exception ex)
        {
            result.IsSucceed = false;
            result.Message = ex.ToString();
        }
        return result;
    }

Upvotes: 2

ReyAnthonyRenacia
ReyAnthonyRenacia

Reputation: 17613

I think this feature is not yet supported. If you check the Download Files guide from Drive API, there's no mention of downloading multiple files at once. That's because you have to make individual API requests for each file. This is confirmed in this SO thread.

But that selected multiple files are convert into single zip file and download that single zip file which is possible with google drive API. So how can i convert them into single Zip File? please tell me.

Upvotes: 2

Related Questions