How can I serve an HTML file from Azure blob storage in a web app using NodeJS?

Viewed 36

Apologies if this is straight-forward, I'm very much not a software developer!

I have a web app using Node (Node 16.17.0, npm 8.13.2). I have HTML files that I upload to an Azure Blob Storage container.

I would like to serve the files directly from the storage container to a user.

In the past, I've typically used something like this:

app.get(
    '/analysis/example', async (req, res) => {
        const a = path.join(__dirname + '/app/analysis/example_file.html');
        res.sendFile(a);
    }
);

However, I'm struggling a bit finding the appropriate documentation or examples to serve a file from blob storage.

I have managed to find a way to print the list of files and the file itself to the console - so I know for sure that I've managed to gain access properly (Azure documentation is quite good) - but I'm just not sure how to make sure the file is in an appropriate state to be served back.

I've tried this:

// GAIN ACCESS TO THE APPROPRIATE STORAGE ACCOUNT

const blobServiceClient = BlobServiceClient.fromConnectionString(
    process.env.AZURE_STORAGE_CONNECTION_STRING
);
const containerClient = blobServiceClient.getContainerClient(
    process.env.AZURE_STORAGE_CONTAINER
);
const blobClient = containerClient.getBlobClient(
    process.env.AZURE_STORAGE_BLOB
);

// A FUNCTION TO HELP TURN THE STREAM INTO TEXT (TURN THIS INTO SOMETHING ELSE?)

async function streamToText(readable) {
    readable.setEncoding('utf8');
    let data = '';
    for await (const chunk of readable) {
        data += chunk;
    }
    return data;
};

// SERVE THE HTML FILE

app.get(
    '/analytics/example', async (req, res) => {

            // THIS CHUNK SUCCESSFULLY LISTS AVAILABLE FILES IN THE CONSOLE
            // for await (const blob of containerClient.listBlobsFlat()) {
            //     console.log("\t", blob.name);
            // };

            const blobDownload = await blobClient.download(0);
            const blob = await streamToText(blobDownload.readableStreamBody);
            res.sendFile(blob);

        }
    }
);

I've also tried the final chunk below (I found an online resource that mentioned that DOMParser wouldn't work with Node):

// SERVE THE HTML FILE

app.get(
    '/analytics/example', async (req, res) => {

            // THIS CHUNK SUCCESSFULLY LISTS AVAILABLE FILES IN THE CONSOLE
            // for await (const blob of containerClient.listBlobsFlat()) {
            //     console.log("\t", blob.name);
            // };

            const blobDownload = await blobClient.download(0);
            const blob = await streamToText(blobDownload.readableStreamBody);
            
            var DOMParser = require('xmldom').DOMParser;
            let parser = new DOMParser();
            let doc = parser.parseFromString(blob, 'text/html');
            res.sendFile(doc.body);

        }
    }
);

Any help much appreciated.

1 Answers

I've just worked it out - it was simply the "res.sendFile" part, should have been "res.send".

The below is the correct working code to read the file from Azure Storage and serve it back to the app.

// GAIN ACCESS TO THE APPROPRIATE STORAGE ACCOUNT

const blobServiceClient = BlobServiceClient.fromConnectionString(
    process.env.AZURE_STORAGE_CONNECTION_STRING
);
const containerClient = blobServiceClient.getContainerClient(
    process.env.AZURE_STORAGE_CONTAINER
);
const blobClient = containerClient.getBlobClient(
    process.env.AZURE_STORAGE_BLOB
);

// A FUNCTION TO HELP TURN THE STREAM INTO TEXT (TURN THIS INTO SOMETHING ELSE?)

async function streamToText(readable) {
    readable.setEncoding('utf8');
    let data = '';
    for await (const chunk of readable) {
        data += chunk;
    }
    return data;
};

// SERVE THE HTML FILE

app.get(
    '/analytics/example', async (req, res) => {
            const blobDownload = await blobClient.download(0);
            const blob = await streamToText(blobDownload.readableStreamBody);
            res.send(blob);
        }
    }
);
Related