◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●SDK 58 ISSUE — Android ウィジェットが定期更新されない、Stripe が静的リンクになるなど初期報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●SDK 58 ISSUE — Android ウィジェットが定期更新されない、Stripe が静的リンクになるなど初期報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/開発ツール
⬡ 開発ツール/2026-10-22中級

「容量不足」と出た動画の保存。空き容量を疑う前に、ファイルが 2GB を超えていないかを見ます

Android で 2.2GB の動画だけがギャラリーに保存できず、「Not enough space」と出る。空き容量は十分なのに、です。Expo の公開 Issue を読み、同じ境目をこの手元の Linux で動かして確かめました。保存前に大きさを見る数行と、分けてコピーする考え方をまとめています。

Expo225expo-media-library4Android59大きなファイルトラブルシューティング92

録画機能を持つアプリで、短い動画はギャラリーに保存できるのに、長く撮ったものだけが保存されません。エラーには「容量が足りません」と出ます。端末の空きは数十 GB 残っているのに、という報告を、Expo の公開 Issue で読みました。

私は個人開発で、画像を端末に保存する機能を何度も書いてきました。エラー文を信じて空き容量の表示ばかり見て、半日を使った経験があります。この Issue を読んだとき、その半日が頭をよぎりました。

最初にお伝えしたいのは、「容量不足」という文言が、容量の話とは限らないということです。 ここからは、報告されている内容を整理し、境目の数字をこの手元で実際に動かして確かめ、アプリ側で先に確かめておく数行を書き残します。

報告されている現象

Expo の Issue(expo/expo #47767)に、expo-media-library の createAssetAsync が Android で大きなファイルを保存できない、という報告があります。環境は expo ~55.0.27、expo-media-library ~55.0.18、react-native 0.83.6 です。iOS では起きないと書かれています。

エラー文は「Unable to copy file into external storage.」で、原因として「Not enough space」が添えられています。報告者がアプリのキャッシュにダミーの mp4 を作って試した結果は、次のとおりです。

ファイルの大きさ保存の結果
1.00 GB保存できた
2.10 GB保存できた
2.20 GB失敗した
4.00 GB失敗した

空き容量は足りている状態での結果だと、報告者は書いています。つまり、境目は端末の空きではなく、ファイル自体の大きさにあります。Issue の時点では未修正で、メンテナの方が main で原因を確認した、という状態でした(2026-10-22 に確認)。

境目の数字は、どこから来るのか

報告者の見立ては、保存処理の中で FileChannel.transferTo を 1 回だけ呼んでいて、ループしていない、というものです。Linux の sendfile は 1 回の呼び出しで運べる量に上限があり、その値は 2,147,479,552 バイトです。1 回で運び切れないと、残りが「書けなかった」と受け取られ、作りかけの項目が消されて、容量不足の文言になる——という流れです。

ここは Android の内部の話で、私の手元では再現できません。そこで、同じ上限が本当にあるのかだけを、Linux の同じ仕組み(sendfile)で確かめました。2.2GB(2,306,867,200 バイト)の空ファイルを作り、1 回だけ呼んだ結果です。

import os
 
size = os.path.getsize("src")          # 2,306,867,200 バイト
s = os.open("src", os.O_RDONLY)
d = os.open("dst1", os.O_WRONLY | os.O_CREAT)
 
n = os.sendfile(d, s, 0, size)         # 1 回だけ呼ぶ
print(n, size - n)
# 2147479552 159387648

1 回で運ばれたのは 2,147,479,552 バイトちょうどで、159,387,648 バイトが残りました。Int32 の最大値(2,147,483,647)より、4,095 バイト手前です。数字の境目が、Issue の「2.10GB は通り、2.20GB は通らない」と同じ場所にあることが分かります。

続けて、残りがなくなるまで繰り返す形に変えました。

d = os.open("dst2", os.O_WRONLY | os.O_CREAT)
off = 0
while off < size:
    n = os.sendfile(d, s, off, size - off)
    if n == 0:                          # 進まないときは本当の失敗として止める
        raise RuntimeError("no progress")
    off += n
print(off == size)                      # True(2 回の呼び出しで完了)

2 回の呼び出しで 2.2GB すべてが運ばれました。Issue で提案されている直し方も、同じ考え方です。「1 回で全部運べる」と決めつけず、運べた分だけ進めて続けること。 そして、まったく進まないときだけを本当の失敗として扱います。

アプリ側で、先にできること

ライブラリ側の修正を待つあいだ、アプリ側でできることは二つあります。どちらも、原因を切り分けるための手がかりを増やすものです。

一つめは、保存の前にファイルの大きさを見て、上限を超えていたら、別の文言で知らせることです。

import * as FileSystem from "expo-file-system";
import * as MediaLibrary from "expo-media-library";
 
// 約 2GB。Issue で報告されている境目の少し手前に置いています
const SAFE_LIMIT_BYTES = 2_000_000_000;
 
export async function saveVideoToGallery(uri: string) {
  const info = await FileSystem.getInfoAsync(uri);
  if (!info.exists) {
    throw new Error("保存する動画が見つかりません");
  }
  if (info.size > SAFE_LIMIT_BYTES) {
    // 空き容量の話ではない、と利用者に伝わる文言にします
    throw new Error(
      "この動画は大きすぎて、端末のギャラリーに保存できません。短く分けて保存してください。"
    );
  }
  return MediaLibrary.createAssetAsync(uri);
}

この断片は、型と流れを示すためのもので、私の手元の端末で createAssetAsync の 2GB 超を再現して確かめたものではありません。上限の値は、Issue の報告に合わせて少し手前に置いた目安です。お使いの SDK の版で、実機の確認をお願いいたします。

二つめは、録画の側で、そもそも 1 本が大きくなりすぎないようにすることです。長時間の録画を、一定の長さで区切って保存する設計にしておけば、この境目には近づきません。

切り分けの順番

「容量不足」と出たときに、私はいま次の順で見ています。

  1. 失敗したファイルの大きさを、バイトで確かめる
  2. 同じ中身の小さなファイルが保存できるかを試す
  3. 小さいものは通り、大きいものだけ落ちるなら、容量ではなくサイズの境目を疑う

空き容量を調べるのは、そのあとです。エラー文は、書いた人の想定した原因を指しているだけで、実際の原因とは限らないのだと、いまは思っています。当時の私は、そこに気づくまでに半日かかりました。私自身、この順番を決めてから、同じ場所で迷う時間が短くなりました。

なお、この記事で動かして確かめたのは、手元の Linux での sendfile の挙動までです。Android 端末での再現は、Issue の報告を引用した範囲にとどまることを、あらためてお断りしておきます。大きな動画を扱うアプリでは、保存の前後で何を記録しておくと原因を追いやすいか、という点も一緒に決めておくと安心です。たとえば、失敗した瞬間のファイルサイズ、保存先の種類、OS の版の三つを、ログに一行で残しておきます。

関連して、保存の権限やアルバムの扱いで詰まったときは、メディア保存の権限とアルバム設計や、端末への保存の不具合の直し方もあわせてご覧ください。

エラー文は読んだうえで、数字は自分で確かめます。 次に保存が落ちたときは、まずファイルの大きさをバイトで一行出力するところから始めてみていただければと思います。私もそこから見るようにしています。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

⬡ 開発ツール2026-10-07
日本語の見出しだけ太字にならない — Android で fontWeight が効かないときの切り分けと直し方
Rork で作ったアプリで、iPhone では太字なのに Android では日本語の見出しが細いままになる症状の切り分け記事です。原因の見分け方、fontFamily を重みごとに分ける直し方、確認用コードを載せています。
⬡ 開発ツール2026-10-21
iOS では時刻どおりに変わるウィジェットが、Android では「Initial」のまま動かない
expo-widgets の updateTimeline は Android では何も起こさず、生成される XML の updatePeriodMillis も 0 です。症状の見分け方、updateSnapshot で補える範囲、設計を切り替える判断と確認スクリプトをまとめました。
⬡ 開発ツール2026-10-15
EXPO_PUBLIC_ の値が、開発では読めて本番だけ undefined になる書き方を、変換結果で見分けます
API の URL が開発中は読めるのに、ビルドした途端に undefined になる。原因は process.env の書き方でした。babel-preset-expo の変換結果を開発・本番で並べ、置き換わる書き方と残ってしまう書き方を表にしました。