6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[JavaScript] FileReader を使わない

Last updated at Posted at 2024-09-18

元々は BlobFile の内容を読み込むのに FileReader が必要でしたが、最新版の JavaScript では不要です。

参考「FileReader - Web APIs | MDN
参考「Blob - Web APIs | MDN
参考「File - Web APIs | MDN

1. 読み込み

1.1. テキスト

1.1.1. UTF-8

悪い例
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const text = await new Promise((resolve, reject) => {

	const reader = new FileReader();

	reader.onerror = () => reject(reader.error);
	reader.onload = () => resolve(reader.result);

	reader.readAsText(blob);

});

console.log(text);
良い例
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const text = await blob.text();

console.log(text);

1.1.2. UTF-8 以外

悪い例
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const text = await new Promise((resolve, reject) => {

	const reader = new FileReader();

	reader.onerror = () => reject(reader.error);
	reader.onload = () => resolve(reader.result);

	reader.readAsText(blob, 'shift-jis');

});

console.log(text);
良い例
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const arrayBuffer = await blob.arrayBuffer();

const textDecoder = new TextDecoder('shift-jis');
console.log(textDecoder.decode(arrayBuffer));

1.2. ArrayBuffer

悪い例
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const arrayBuffer = await new Promise((resolve, reject) => {

	const reader = new FileReader();

	reader.onerror = () => reject(reader.error);
	reader.onload = () => resolve(reader.result);

	reader.readAsArrayBuffer(blob);

});

const textDecoder = new TextDecoder();
console.log(textDecoder.decode(arrayBuffer));
良い例
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const arrayBuffer = await blob.arrayBuffer();

const textDecoder = new TextDecoder();
console.log(textDecoder.decode(arrayBuffer));

1.3. URL

FileReader を用いることでデータ URL を得られますが、データ URL を使用することによって様々な問題が発生する可能性があります。

特に理由がなければオブジェクト URL を使用する方が安全です。

悪い例
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const url = await new Promise((resolve, reject) => {

	const reader = new FileReader();

	reader.onerror = () => reject(reader.error);
	reader.onload = () => resolve(reader.result);

	reader.readAsDataURL(blob);

});

console.log(url);
良い例
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const url = URL.createObjectURL(blob);

console.log(url);

URL.revokeObjectURL(url);

パーセントエンコードまたは Base64 エンコードを用いてデータ URL を生成することも可能です。

const blob = new Blob(['foo\n'], { type: 'text/plain' });

const text = await blob.text();
const url = 'data:text/plain,' + encodeURIComponent(text);

console.log(url);
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const arrayBuffer = await blob.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
const binaryString = uint8Array.reduce(
	(binaryString, uint8) => binaryString + String.fromCharCode(uint8),
	'',
);
const url = 'data:text/plain;base64,' + btoa(binaryString);

console.log(url);

参考「データ URL - URI | MDN
参考「URL: createObjectURL() 静的メソッド - Web API | MDN
参考「Base64 - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN
参考「[JavaScript] Unicode 文字列やバイナリデータを Base64 エンコードおよびデコードする - Qiita

2. 読み込み中

ReadableStream を使用することで読み込み中の処理を行えます。

悪い例
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const text = await new Promise((resolve, reject) => {

	const reader = new FileReader();

	reader.onerror = () => reject(reader.error);
	reader.onload = () => resolve(reader.result);

	reader.onprogress = event => {
		if ( event.lengthComputable ) {
			// 進捗状況
			console.log('%d / %d', event.loaded, event.total);
		}
	};

	reader.readAsText(blob);

});

console.log(text);
良い例: チャンクごとの場合
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const readableStream = new ReadableStream({
	loaded: 0,
	total: blob.size,
	async start(controller) {
		const readableStream = await blob.stream();
		for await (const chunk of readableStream) {
			controller.enqueue(chunk);
			// 進捗状況
			this.loaded += chunk.length;
			console.log('%d / %d', this.loaded, this.total);
		}
		controller.close();
	},
});

const text = await new Response(readableStream).text();

console.log(text);
良い例: 約 50ms ごとの場合
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const readableStream = new ReadableStream({
	loaded: 0,
	total: blob.size,
	intervalID: 0,
	progress() {
		// 進捗状況
		console.log('%d / %d', this.loaded, this.total);
	},
	async start(controller) {
		// 進捗状況
		this.intervalID = setInterval(() => this.progress(), 50);
		// 
		const readableStream = await blob.stream();
		for await (const chunk of readableStream) {
			controller.enqueue(chunk);
			// 進捗状況
			this.loaded += chunk.length;
		}
		controller.close();
		// 進捗状況
		clearInterval(this.intervalID);
		this.progress();
	},
	cancel() {
		// 進捗状況
		clearInterval(this.intervalID);
	},
});

const text = await new Response(readableStream).text();

console.log(text);

FileReader の progress イベントは約 50ms ごとに発生します。

参考「ReadableStream - Web API | MDN
参考「6.2. The FileReader API - File API」(「約 50ms」)

3. 読み込み中止

ReadableStream を使用することで読み込みを中止出来ます。

悪い例
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const text = await new Promise((resolve, reject) => {

	const reader = new FileReader();

	reader.onabort = () => resolve();
	reader.onerror = () => reject(reader.error);
	reader.onload = () => resolve(reader.result);

	reader.readAsText(blob);

	// 中止
	reader.abort();

});
良い例
const blob = new Blob(['foo\n'], { type: 'text/plain' });

const readableStream = await blob.stream();

// 中止
await readableStream.cancel();

参考「ReadableStream - Web API | MDN」。

4. 関連記事

参考「イマドキな JavaScript で書かない・使わないもの: var, function, then, jQuery, その他 - Qiita

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?