大容量サイトを分割エクスポートするLUXCMの仕組み
大容量サイトの分割エクスポート――アップロード上限とパート分割の原理
私は、分割エクスポートを単なる追加機能ではなく、コア機能として位置づけている。
サイトが少し大きくなっただけでも、この機能がなければ、インポート画面までファイルをアップロードすること自体が不可能になるケースを実際に見てきたからだ。
この記事では、なぜこのような問題が起きるのか、そして私がどのように解決したのかを、コードに実装された検証ロジックとともに説明する。

アップロード上限は気まぐれな制限ではなく、共有リソースを守るための仕組みだ
「最近のホスティングなら、大きなファイルでも普通にアップロードできるのでは」と考える人には、これまで何度も出会ってきた。
Bluehostによる共有ホスティングのアップロード上限に関する解説は、なぜその考えが正しくないのかを明確に説明している。実際の上限は「ホスティング会社や料金プランによって異なるが、通常は数MBから128MBの範囲」だという。
そして、この上限は恣意的に設定されているわけではない。複数のサイトが1台のサーバーリソースを共有する共有ホスティングでは、「無制限のアップロード1件がリソースプールを長時間占有し、同じサーバー上にある他のすべてのサイトを遅くしたり、停止させたりする可能性がある」からだ。
upload_max_filesize、post_max_size、memory_limitを保守的に設定するのは、WordPressの制約ではなく、「共有環境に組み込まれたリソース保護機構」だというのが、この記事の結論である。この説明を読んで、ユーザーに「ホスティング会社へ依頼して上限を引き上げてもらいましょう」と案内することは、根本的な解決策にはならないと改めて確認した。
これはホスティング会社が不親切だからではなく、同じサーバーを利用する他のサイトを守るための意図的な設計だからだ。そこで私は上限を回避しようとするのではなく、その制限の範囲内で処理を完了できるよう、ファイル自体を細かく分割する方法を選んだ。
デフォルト値を19MB、調整範囲を1~500MBとした理由
エクスポート画面では、1つのパートの最大サイズはデフォルトで19MBに設定されている。「詳細設定」を有効にすれば、1MBから500MBの範囲で自由に変更できる。
これは「誰にとっても正解となる設定が1つあるわけではない」ことを認め、ユーザーが自分のホスティング環境に合わせて調整できるようにしたものだ。
ファイル名の先頭に番号を付けた理由
パートファイルの名前は、これまでのNEWS-LUX-20260825-074327.wpmpx.part1of2のように末尾へ番号を付ける形式から、part1of2-NEWS-LUX-20260825-074327.wpmpxのように先頭へ番号を付ける形式へ変更した。

番号を先頭に移すと、複数のパートを一覧表示した際、ファイル名の最初の文字を見るだけで順番がすぐに分かる。ただし、旧バージョンが生成した「末尾に番号を付ける」形式のファイルも引き続き認識できるよう、2種類のパターンをどちらも解析する仕様にした。すでにその形式でパートファイルを作成しているユーザーがいる可能性があるからだ。
再結合は「とりあえず結合してみる」方式ではない
インポート画面でパートをまとめて選択してアップロードしても、私はすぐに結合することはしない。まず、次の検証を順番に行う。
1つ目は、パートファイルではないファイルが混ざっていないかの確認だ。ファイル名がpartN of M-パターンに一致しない場合は、「パートファイルではないファイルが混在しています」というエラーを直ちに表示する。
2つ目は、異なるエクスポート処理で作成されたパートが混ざっていないかの確認だ。すべてのパートは同じベースファイル名と同じ総パート数を持っていなければならない。これらが一致しない場合は、「異なるエクスポートセットのパートが混在しています」と通知する。これは、ユーザーが誤って昨日エクスポートしたファイルと、今日あらためてエクスポートしたファイルのパートを混ぜてアップロードする事態を防ぐためだ。
3つ目は、同じ番号のパートが重複してアップロードされていないかの確認だ。たとえばpart2of5が2つアップロードされ、part3of5が存在しない場合は、「同じ番号のパートが重複してアップロードされています」と通知する。
この4つの検証のうち、どれか1つでも失敗した場合、処理を黙って続行することはない。不完全または不正なファイルでインポートを試みるよりも、何が問題なのかを最初に正確に伝え、処理を止めるべきだと判断したからだ。
本当の意味での「レジューム可能なアップロード」ではないことも、正直に伝える必要がある
この分割方式は、Filestackの大容量アップロード処理ガイドが説明する「チャンクアップロード」とは明確に区別しておきたい。
このガイドは、ファイル全体を1回のリクエストでアップロードする際に生じる問題を指摘している。遅い、あるいは不安定なネットワーク環境では「サーバーの実行時間制限によってアップロードが完了しない」タイムアウトの問題、接続が切れると「最初からやり直さなければならない」再試行コストの問題、さらに「大容量ファイルをメモリ上で処理する共有サーバーが利用可能なリソースを使い果たし、最終的にサーバーが停止する」問題だ。このガイドが解決策として挙げているtusやResumable.jsのような本来のチャンクアップロードプロトコルでは、1つのファイルをバイト単位で分割して送信し、接続が途切れた場合も中断した地点から正確に再開できる。
私が実装した分割方式は、それとは異なる。
バイトストリームの途中で接続が切れた際に、その地点から引き継ぐ本来の意味での「レジューム可能なアップロード」ではない。ただし、Filestackのガイドが指摘する問題の多くは、この方式でも実質的に解決できる。
1つのパートを小さくすることで、サーバーの実行時間制限にかかるリスクを抑えられる。また、アップロード中にパート1つの処理が失敗しても、数百MB全体を最初からアップロードし直す必要はなく、そのパートだけを再送すればよい。
さらに、再結合の際もパートファイルを一度にメモリへ読み込んで連結するのではなく、stream_copy_to_streamを使って1つずつ順番に読み書きする。そのため、パート数や全体のサイズにかかわらず、再結合時のメモリ使用量は一定に保たれる。私はこれを「完全なレジューム可能アップロード」と誇張したくはない。
その代わり、実際に何をしているのか――ファイル自体を複数の独立したパートに分割し、それらを厳密に検証したうえで、メモリに負担をかけず順番どおりに再結合すること――を正確に伝える道を選んだ。
結論として
分割エクスポートは、ホスティング会社ごとに異なるものの、そこには合理的な理由があるアップロード上限という現実を前提に設計されている。
デフォルト値と調整可能な範囲は、実際のホスティング環境の分布を考慮して決めた。ファイル名における番号の位置は、実際の使い勝手に関する問題から導いた判断だ。
これが本来の意味でのレジューム可能なアップロードではないことも、隠すつもりはない。それでも、この程度の設計によって、大容量サイトを移行しようとしてアップロード上限の前で途方に暮れる状況は、確実に減らせると考えている。
参考資料
- Bluehost — File Upload Limit Problems on Shared Hosting: Why They Happen and How to Fix Them
- Filestack — Solving Common Problems Encountered When Handling Large File Uploads
