1
0

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で扱う

1
Posted at

ダウンロードの進捗を表示するとき、総バイト数が分からない応答に「0%」を出すと、通信が止まっているように見えます。受信量が分かることと、全体に対する割合が分かることは別です。今回は進捗イベントを表示用モデルへ変換し、不明な総量を明示する小さな関数を作ります。

私は Twitter Viewer を開発しています。公開動画を MP4 として保存する twitter video downloader のような UI を考える際にも、メディア情報取得とファイル転送の状態は分けて扱います。以下は一般的なサンプルで、サービス内部の実装ではありません。

総量が不明なら割合を返さない

XMLHttpRequest の進捗イベントには loaded、total、lengthComputable があります。計算可能でないイベントに仮の総量を入れて割合を作らず、不定状態を返します。fetch のストリームを自分で読む場合も、ヘッダーの値を総量として利用できるかは別に判断する必要があります。

export function progressModel(event) {
  const loaded = Number.isFinite(event.loaded) && event.loaded >= 0
    ? event.loaded : 0;
  const known = event.lengthComputable === true &&
    Number.isFinite(event.total) && event.total > 0 &&
    loaded <= event.total;
  return {
    loaded,
    total: known ? event.total : null,
    percent: known ? Math.floor(loaded / event.total * 100) : null,
    label: known
      ? `${Math.floor(loaded / event.total * 100)}%`
      : `${loaded} bytes received`
  };
}

この関数はバイト数をそのまま表示します。実際の画面では KB や MB に整形しても構いませんが、単位の規則を統一します。総量より受信量が多い入力は、100% に丸めて隠さず「割合不明」へ戻す方針です。メタデータと実際の転送量が一致しない場合に、誤った精度を見せないためです。

境界値をネットワークなしで確かめる

関数を progress.mjs、以下を progress.test.mjs に保存します。

import test from 'node:test';
import assert from 'node:assert/strict';
import { progressModel } from './progress.mjs';

test('総量が分かる場合だけ割合を返す', () => {
  assert.equal(progressModel({ loaded: 25, total: 100,
    lengthComputable: true }).percent, 25);
  assert.equal(progressModel({ loaded: 25, total: 0,
    lengthComputable: false }).percent, null);
});

test('ゼロと不整合を分ける', () => {
  assert.equal(progressModel({ loaded: 0, total: 100,
    lengthComputable: true }).percent, 0);
  assert.equal(progressModel({ loaded: 101, total: 100,
    lengthComputable: true }).percent, null);
  assert.equal(progressModel({ loaded: NaN }).loaded, 0);
});

node --test progress.test.mjs で実行できます。総量不明の応答を実際に用意しなくても、表示の判断を確認できます。このテストは転送速度やサーバーの挙動を検証するものではありません。

progress要素にも不定状態がある

HTML の <progress> は value 属性がない場合に不定状態を表せます。総量が分からない間は value="0" を固定する代わりに、属性を外す方法があります。周囲の文章で「受信中、総量不明」と説明し、受信済みの量を補助情報として出します。

割合が分かるようになったら max="100" と計算した value を設定します。スクリーンリーダー向けに毎バイト更新を読み上げる必要はありません。短い間隔の変更を集約し、開始、失敗、完了のような意味のある変化を伝えます。

100%と保存完了を同一視しない

この割合は、扱っている転送の受信量に関する情報です。受信後に変換や検証があるなら、それらの処理は別状態です。また、通常のリンクでブラウザーのダウンロード管理へ渡した場合、ページ側が最終保存まで観測できるとは限りません。

表示には「受信完了」「変換中」「ブラウザーで保存先を確認」のように確認できた事実を使います。ユーザーが実際に保存したと確認できていない段階で、「端末への保存完了」を表示しないことが、このモデルの使い方の境界です。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?