変更歴
2022/09/21
- `reader.onload`のところを、`reader.addEventListener("load", () => ...)`というように変更しました。
通常、JavaScriptではel.onclick = () => {}
というように、.onを使ってEventListenerを追加するやり方は推奨されていないのです。 - Promiseの型にstringの型を追加しました。
TypeScriptインタプリタはエラーを吐くので、resolveする前に型チェックを入れる必要があります。
概要
フロントエンドのWebアプリでユーザーが指定したファイルをアップロードする前に、Base64に変換することが常套手段です。
その理由は、Base64はファイルサイズが多少大きくなるが、データ破壊を防げるからです。
2進コードファイルをブラウザでBase64に変換するには、FileReader APIを使います。
ただ、このFileReader APIはかなり古いようで、なんと、Promiseに対応していないのです!
そこで、そのFileReader APIをPromiseに包む、Promise化の方法をここでご紹介します。
コード
ブラウザのHTMLInputElementから取ったFileListを引数としてもらいます。
ファイル一つのみの場合は以下のように使います。
const convertToB64 = (filesList: FileList) =>
new Promise<string>((resolve, reject) => {
const file = filesList[0];
const reader = new FileReader();
reader.addEventListener("load", () => {
const { result } = reader;
if (typeof result !== "string") throw TypeError("Reader did not return string.");
resolve(result);
});
reader.addEventListener("error", () => {
reject(reader.error);
});
reader.readAsDataURL(file);
});
複数の場合はPromise.allを使います。
const convertAllToB64 = (filesList: FileList) =>
Promise.all(
Array.from(filesList).map(
(file) =>
new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.addEventListener("load", () => {
const { result } = reader;
if (typeof result !== "string") throw TypeError("Reader did not return string.");
resolve(result);
});
reader.addEventListener("error", () => {
reject(reader.error);
});
reader.readAsDataURL(file);
})
)
);
これらのPromiseが解決されたら、Fetch APIなどでサバーに送るなりすればいいです。
注意点
FileReader APIのreadAsDataURLは、Data URI Schemeになっています。Base64の前に、data:image/png;base64,を追加しているのです。
これをsplit(",")[1]などでBase64だけを取ることができます。
このままBase64からHexなどに変換しようとすると、フォーマットが変になるのでご注意ください。
まとめ
以上、FileReader APIの簡単なPromise化方法をご紹介しました!FileReader APIには他のフォーマットに変換する機能があるので、必要に応じて変えればいいと思います。