I had a hard time figuring out how to download from a Blob Storage with limited Access Levels using NextJs 14 with App Router. In my case it was because Typescript acted a bit bizzare when it came to return SearchParameters and instanciate a Response object with a readable stream.
Infact, while passing the readableStream to the Response constructor, like this:
const blobStream = blobDownloadResponse.readableStreamBody;
return new Response(blobStream as any, {
Typescript complained that
Argument of type 'ReadableStream | undefined' is not assignable to parameter of type 'BodyInit | null | undefined'. Type 'ReadableStream' is not assignable to type 'BodyInit | null | undefined'.
This is a lie, and simply having TS shut up with any
solved the issue, and the download actually works:
import { BlobServiceClient } from "@azure/storage-blob";
import { NextResponse } from "next/server";
export async function GET(req: Request) {
try {
const filename = (req as any).nextUrl.searchParams.get('fileName')
const connString = process.env.STORAGE_ACCOUNT_CONNSTRING
const instance = BlobServiceClient.fromConnectionString(connString);
const containerClient = instance.getContainerClient(process.env.ZIP_CONTAINER);
const blobClient = containerClient.getBlobClient(filename!);
const blobDownloadResponse = await blobClient.download();
const blobStream = blobDownloadResponse.readableStreamBody;
return new Response(blobStream as any, {
headers: {
"content-disposition": `attachment; filename="${filename}"`,
"Content-Type": "application/zip"
}
})
} catch (error) {
console.error('Error:', error);
return NextResponse.json(
{ error: 'error occurred while downloading the file.' },
{status:500}
)
}
};
As stated by the Response Web Api Doc, the constructor can accept a ReadableStream, like the one returned by the BlobDownloadResponseParsed interface, yet Typescript came out with this error:
Argument of type 'ReadableStream | undefined' is not assignable to parameter of type 'BodyInit | null | undefined'.
Type 'ReadableStream' is not assignable to type 'BodyInit | null | undefined'.
which led me to errors, misunderstanding and time waste.
return new Response(blobStream as any, {
did the trick for me.
I know it's baaaaad to use any
in TS (in that case one might as well just use simple JS instead) but i could not figure out another solution.
I used as reference: