先に結論
- Azure SDK を用いてPDFファイルをアップロードした場合、ContentTypeのデフォルト値は
application/octet-stream
になる模様 - ContentTypeが
application/octet-stream
のBlobの場合、WEBブラウザから当該BlobのURLにアクセスすると、プレビューではなくファイルダウンロードに遷移してしまう- → ContentTypeを
application/pdf
に変えればプレビュー可能 - この差分は、ブラウザ側で参照データのContentTypeを元に振る舞いを制御していることに起因する
- → ContentTypeを
-
@azure/storage-blob
などのAzure Storage クライアントライブラリを用いてファイルをアップロードしている場合、Blobアップロード関数のOptionでblobHTTPHeaders
のblobContentType
を設定することで任意のContentTypeを指定できる
検証条件
- 言語:TypeScript
- ランタイム:Node.js
- Azure Storageクライアントライブラリ:@azure/storage-blob
やりたいこと
- WEBアプリからPDFファイルをBlob Storageにアップロードしたい
- アップロードしたPDFファイルのURLを取得し、WEBアプリの任意のボタンを押すと当該ファイルをWEBブラウザ上で開くようにしたい
やったこと:PDFファイルのアップロード(修正前)
上記のMS Learnを参考に、BlockBlobClient
を生成し、uploadStream
関数を用いてPDFファイル(Readableストリームに変換済み)をBlob Storageにアップロードする。
const uploadDataAsPdf: (data: File) => Promise<void> = async (data) => {
/** アップロード先 Azure Blob Storage コンテナ名 */
const containerName = "upload-test";
/** アップロードするPDFファイル名 */
const blobName = "upload-test.pdf";
// File型の data をストリームに変換
const fileStream = data.stream();
const reader = fileStream.getReader();
/** アップロードするPDFファイルデータ */
const targetData: Readable = new Readable({
async read() {
const result = await reader.read();
if (result.done) {
this.push(null);
} else {
this.push(Buffer.from(result.value));
}
},
});
/** アップロード先 Azure Blob Storage の接続文字列 */
const clientUrl = 'hogehogehogehoge';
/**
* Blob サービスクライアントを作成
* @see https://learn.microsoft.com/ja-jp/javascript/api/overview/azure/storage-blob-readme?view=azure-node-latest#using-connection-string
*/
const blobServiceClient = BlobServiceClient.fromConnectionString(clientUrl);
/** コンテナクライアントインスタンス */
const containerClient = blobServiceClient.getContainerClient(containerName);
/** Block Blobクライアント */
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
// Blob StorageにPDFファイル(Readbaleストリーム)をアップロード
await blockBlobClient.uploadStream(stream);
}
この関数を用いて、リクエストボディで受け取ったFileデータをAzure Blob StorageにアップロードするPOST APIを作成。
お試ししてみると……
ということでやりたいこと1「WEBアプリからPDFファイルをBlob Storageにアップロードしたい」はクリアー。
ではアップロードしたPDFファイルのURLにブラウザからアクセスしてみよう!
ダウンロードポップアップが開いた?????
やりたいこと2は「アップロードしたPDFファイルのURLを取得し、WEBアプリの任意のボタンを押すと当該ファイルをWEBブラウザ上で開く」。このままではWEBブラウザ上でのプレビューが実現できない!
よわよわエンジニア、ContentType(MIMEタイプ)を知る
よわよわエンジニアがあれこれネットの海に問い合わせたところ、どうやらブラウザでPDFをプレビューするにはContentTypeをapplication/pdf
にする必要があるっぽいことを発見。
ContentTypeは、ファイルやインターネット上で転送されるデータの形式を識別するためのコード。
MIMEタイプとも表記される。
よくAPIレスポンスなどで目にする text/plain
やapplication/json
、image/jpeg
などもContentTypeの一種。
今回はFile(=バイナリデータ)をアップロードしたが、この場合のContentTypeのデフォルト値はapplication/octet-stream
になる模様。拡張子で言うと.bin
が相当。任意の種類のバイナリーデータならなんでもapplication/octet-stream
。
↑たしかに upload-test.pdf のContentTypeはapplication/octet-stream
である……。
そして、「ダウンロードポップアップの表示」は、参照したデータのContentTypeがapplication/octet-stream
の場合のブラウザ側の挙動だった。
もしデータのContentTypeがapplication/pdf
になっていると、ブラウザ側がPDFファイルに対するデフォルトの振る舞いをしてくれる。それが今回の期待値である「PDFのブラウザ上でのプレビュー」だったというわけである。
やったこと:PDFファイルのアップロード(修正後)
ということで、Blobストレージにアップロードする際、明示的に「このBlobはPDFなんですよ!」と示してあげれば良いと判明。
今回用いていた Azure Storageクライアントライブラリ@azure/storage-blob
の uploadStream
関数のリファレンスを確認する。
第4引数options
に指定するBlockBlobUploadStreamOptions
の詳細を確認すると、この中にblobHTTPHeaders
という、blobContentType
とやらを設定するプロパティがあることがわかる。
ということで、先ほどのソースのうちuploadStream
関数を呼び出していた部分を以下の通り修正。
// Blob StorageにPDFファイル(Readbaleストリーム)をアップロード
- await blockBlobClient.uploadStream(stream);
+ await blockBlobClient.uploadStream(
+ stream,
+ undefined, // bufferSize :デフォルト値「8」を使用
+ undefined, // maxConcurrency :デフォルト値「5」を使用
+ {
+ blobHTTPHeaders: { blobContentType: "application/pdf" }, // PDFとしてアップロード
+ },
+ );
これで再度試してみると……
↑ upload-test.pdf のContentTypeがapplication/pdf
になっている!
↑ URLにアクセスすると、ちゃんとPDFをプレビューできている!
結論
Azure Blob StorageのようなBlobサービスを用いる際は、当該サービスの概念(コンテナとか仮想ディレクトリとか)を押さえることも、Azure SDKを使えることも大事であるが、
まずは扱うデータの特性や仕様自体をちゃんと押さえる必要がある(当たり前体操)
動いたやったね!!! で終わらず、「で、私いまPDFファイルを何をどうしてStorageにアップロードできたの??? FileってなにBlobってなに???」……など自分が書いたコード/実施した操作の内容をちゃんと把握しよう。。