GauthamGAjith
GauthamGAjith

Reputation: 377

Download multiple blob in express js?

I am making a web app with express js. The storage I am using is Azure Blob. The storage structure is like this patient/date/time/images/1.jpg .. 1000.jpg.

I need to have a download button near the patient profile where they will be able to download data only from patient/ folder. Is there is any function that will allow you to getBlobToStream using prefix option?

Thank you.

Upvotes: 0

Views: 1722

Answers (1)

Jerry Liu
Jerry Liu

Reputation: 17800

Save this code snippet as storageoper.js. Add your connection string.

const azure = require('azure-storage');
const archiver = require('archiver');
const storageConnectionString = 'xxx';
const blobService = azure.createBlobService(storageConnectionString);

let prefix = "";
let container = "";
let continuationToken = null;
let blobCount = 0;
let blobResults = [];
let parallelrequest = 100;

// list at most 5000 blobs once
const listBlobsSegmented = () => {
    return new Promise((resolve, reject) => {
        blobService.listBlobsSegmentedWithPrefix(container, prefix, continuationToken, (err, results) => {
            if (err) {
                reject(err);
            } else {
                continuationToken = results.continuationToken;
                blobResults = results.entries;
                resolve("done");
            }
        });
    });
};

const zipOneBlob = (blobName) => {
    return new Promise((resolve, reject) => {
        blobService.createReadStream(container, blobName, err => {
            if (err) {
                reject(err);
            }
        }).on('data', data => {
            zip.append(data, { name: blobName });
            resolve("done");
        }).on('error', err => {
            reject(err);
        });
    });
};

// restrict parallel count of request to storage
const zipBlobsSegmented = (var1, var2) => {

    let zippedCount = var1;
    let leftCount = var2;

    let promises = [];
    let length = leftCount < parallelrequest ? leftCount : parallelrequest;

    for (let i = zippedCount; i < length + zippedCount; i++) {
        let promise = zipOneBlob(blobResults[i].name);
        promises.push(promise);
    }
    return Promise.all(promises).then(() => {
        zippedCount += length;
        leftCount -= length;
        return leftCount > 0 ? zipBlobsSegmented(zippedCount, leftCount) : Promise.resolve();
    });
};

// list and download
const listAndDownloadBlobs = () => {
    return listBlobsSegmented().then(() => {
        return zipBlobsSegmented(0, blobResults.length);
    }).then(() => {
        blobCount += blobResults.length;
        return continuationToken ? listAndDownloadBlobs() : Promise.resolve();
    }).catch(err => {
        console.log(err);
    });
};

let zip = archiver('zip').on('error', error => {
    console.log(error);
});


module.exports.createZipFromBlobs = (res, containerName, blobPrefix, parallelRequestCount = 100) => {
    container = containerName;
    prefix = blobPrefix;
    parallelrequest = parallelRequestCount;
    zip.pipe(res);
    listAndDownloadBlobs().then(() => {
        zip.finalize();
        console.log(`total ${blobCount} files downloaded`);
    });
};

Put it in your web app and import it as a module.(Also Need to install archiver module).

var storageoper = require('[specify relative path]/storageoper');

Then call the method in your app handler.

res.attachment(`${containerName}.zip`);
storageoper.createZipFromBlobs(res, containerName, prefix);

Upvotes: 2

Related Questions