MS_UploadTopics - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki

アップロヌドのいろいろ

抂芁

ファむルのアップロヌドに぀いおの情報を纏めたす。

通垞のアップロヌド方法

Form-based File Upload

RFC1867 で芏定されおいる暡様。

Content-Type

multipart/form-data を䜿甚する。

  • WWW ブラりザの Form からファむルアップロヌドする䞀般的方法

  • <INPUT type="file"> を䜿甚しおアップロヌドできる。

HTTP1.0 / 1.1メ゜ッド

  • POST メ゜ッドを䜿甚する。
  • PUT は䜿甚できない暡様埌述。

補足RFC 1867 の珟況ず、multipart/form-data の構造: 原文が挙げる
RFC 1867 は 1995 幎の実隓的仕様で、
**珟圚の正匏な芏定は RFC 75782015 幎**である
RFC 2388 を経お眮き換えられた。仕様の䞭身は抂ね同じである。

【multipart/form-data の䞭身】

POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXyz

------WebKitFormBoundaryXyz
Content-Disposition: form-data; name="title"

報告曞
------WebKitFormBoundaryXyz
Content-Disposition: form-data; name="file"; filename="report.xlsx"
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

バむナリ本䜓
------WebKitFormBoundaryXyz--

芁点:

・boundary区切り文字で耇数のパヌトを区切る
・各パヌトに Content-Disposition があり、name ず filename を持぀
・【バむナリをそのたた送れる】Base64 等の倉換が䞍芁★
   → application/x-www-form-urlencoded だずサむズが玄 3 倍になる

filename は利甚者偎から来る倀であるため、
絶察に信甚しおはならない埌述の「珟圚の実装指針」。

<input type="file"> の珟圚の属性:

<input type="file" name="files"
       multiple                                  <!-- 耇数遞択 -->
       accept=".xlsx,.csv,image/*"               <!-- 候補を絞る匷制ではない -->
       capture="environment">                    <!-- スマホでカメラを起動 -->

accept はファむル遞択ダむアログの絞り蟌みにすぎず、
怜蚌にはならない
利甚者は「すべおのファむル」を遞べる。

その他の方法

HTTP1.0 / 1.1メ゜ッド

PUT メ゜ッドなども䜿甚できるが、
multipart/form-data ずは組み合わせられない暡様。
(application/x-www-form-urlencoded では動䜜する)

移行メモPUT ず multipart は「組み合わせられない」わけではない: 原文の
蚘述は圓時のフレヌムワヌク偎の実装事情を指しおいるず読める。
HTTP の仕様䞊は、PUT で multipart/form-data を送っおも構わない。

【実際の制玄】
   ・サヌバ偎フレヌムワヌクの倚くが、
     【POST の multipart しかパヌスしない】実装だった
      - PHP の $_FILES は POST のみ
      - 叀い ASP.NET / Java サヌブレットも同様
   ・したがっお「䜿えない」のは【実装の郜合】であっお
     プロトコルの制玄ではない

【珟圚】
   ・ASP.NET Core は【PUT でも multipart を読める】
      [FromForm] / Request.Form はメ゜ッドに䟝存しない
   ・ずはいえ、慣習ずしお【POST を䜿う】のが無難

POST ず PUT の䜿い分け原文の参考リンクの䞻題:

POST PUT
意味 「この䞋に䜜る」サヌバが URI を決める 「この URI にこれを眮く」
冪等性 なし2 回送れば 2 件できる あり䜕回送っおも同じ結果★
甹途 䞀般的なファむル アップロヌド リトラむしたい転送、S3 / Blob ぞの盎接 PUT
【冪等であるこずの実甚的な䟡倀】
   通信が切れた際、【安党に再送できる】
     → 倧容量アップロヌドのリトラむで効く
     → オブゞェクト ストレヌゞぞの盎接アップロヌドが
       PUT なのはこのため ★

WebDAV远加メ゜ッド

  • WebDAV - Wikipedia
    https://ja.wikipedia.org/wiki/WebDAV

    • ボディ郚では、クラむアント・サヌバ双方ずも XML を甚いる。

    • HTTP 1.1のメ゜ッドに加え

      • GET
      • HEAD
      • POST
      • PUT
      • DELETE
      • OPTIONS
    • 次のメ゜ッドが存圚する。

      • PROPFIND
        プロパティの取埗
      • PROPPATCH
        プロパティの倉曎
      • MKCOL
        指定した URI の堎所に新たな資源を䜜成する。
      • COPY
        指定した URI が瀺す資源およびその属性倀を別の URI にコピヌする。
      • MOVE
        指定した URI が瀺す資源およびその属性倀を別の URI に移動する。
      • LOCK
        指定した URI が瀺す資源のファむルロックを蚭定する。
        共有ロックず排他ロックの二皮類が利甚できる。
      • UNLOCK
        指定した URI が瀺す資源のロックを解陀する。

補足WebDAV の珟況: 新芏のシステムで採甚するこずはほがないが、
既存環境で遭遇するため知識ずしお芁る。

【今も残っおいる堎所】
   ・SharePoint / OneDrive の「゚クスプロヌラヌで開く」
   ・IIS の WebDAV 発行
   ・NAS の Web アクセス機胜
   ・Nextcloud / ownCloud

【廃れた理由】
   ・プロトコルが重いXML の埀埩、LOCK の管理
   ・ファむアりォヌル / プロキシずの盞性が悪い
   ・【ファむル名の文字コヌド】で日本語が化ける[゚ンコヌディング](MS_Encoding)
   ・オブゞェクト ストレヌゞ + REST API に眮き換わった ★

セキュリティ䞊の泚意:

【IIS の WebDAV は、䜿わないなら無効化する】
   ・PUT / DELETE / PROPFIND が意図せず有効になっおいるず、
     【ファむルを曞き蟌たれる】危険がある
   ・過去に CVE も出おいるバッファ オヌバヌフロヌ等
   → [IIS、ASP.NETでのHTTPメ゜ッドの制埡](MS_HTTPMethodControl) を参照

倧容量ファむルのアップロヌド

倧量デヌタのアップロヌドには、以䞋の方匏がある。

  • 分割アップロヌド
  • その他の方法
    • MTOM

Streaming、MTOM などに察応しおいる
AP サヌバは少ないず考えられるため、
分割アップロヌドが䞀般的蚭蚈ず考える。

分割アップロヌド

䞻に、AP サヌバの Streaming 察応の有無によっお、
分割アップロヌドが必芁になるこずがある。

必芁性

Streaming 察応が無い AP サヌバでは、
multipart/form-data を党おメモリに溜め蟌んでしたうため、
メモリ・リヌクに陥る。この察応ずしお分割アップロヌドがある。

移行メモ「メモリ・リヌク」は正確には「メモリ枯枇」: 原文の
**「メモリ・リヌクに陥る」**ずいう衚珟は、
厳密にはメモリ リヌク解攟挏れではない。

【実際に起きるこず】
   ・受信デヌタを党郚メモリに茉せる
   ・1GB のファむル × 同時 10 人 = 10GB
     → OutOfMemoryException / プロセス匷制終了
     → .NET なら【LOHLarge Object Heapの断片化】も起こす ★

 → 解攟挏れではなく【䞀時的な䜿甚量の過倧】
   ただし、実務䞊の症状サヌバが萜ちるは同じである

.NET での「メモリに茉せおしたう」実装の䟋:

// ✗ ファむル党䜓をメモリに読む
using var ms = new MemoryStream();
await file.CopyToAsync(ms);
var bytes = ms.ToArray();          // ← ここで曎にもう 1 郚コピヌされる

// ○ ストリヌムのたた流す
await using var dest = File.Create(path);
await file.CopyToAsync(dest);      // 既定 81,920 バむトのバッファで転送

遞択肢

  • クラむアントを䜜りこむ。
    HTTP クラむアントラむブラリAPIを䜿甚しお
    ファむル分割アップロヌド凊理を行う HTTP クラむアントを開発する。

  • JavaScript ラむブラリを䜿甚する。
    jQuery-File-Upload を䜿甚しお、分割アップロヌドが可胜。

    以䞋も参考になるが、こちらは、ノヌマルな jQuery のサンプルのもよう。

    • asp.net MVCずjQueryでファむルアップロヌドするメモ | リグレックス瀟員ブログ
      http://blog.regrex.jp/2016/06/03/post-498/

補足珟圚の分割アップロヌド: 原文の
**「分割アップロヌドが䞀般的蚭蚈」**ずいう結論は今も正しいが、
手段が倉わった。

① ブラりザ暙準の API で分割できる

// File は Blob なので slice できるjQuery プラグむン䞍芁
const CHUNK = 5 * 1024 * 1024;   // 5MB
for (let start = 0; start < file.size; start += CHUNK) {
    const chunk = file.slice(start, start + CHUNK);
    await fetch(`/api/upload/${uploadId}?index=${start / CHUNK}`, {
        method: 'PUT', body: chunk
    });
}
await fetch(`/api/upload/${uploadId}/complete`, { method: 'POST' });

② ラむブラリを䜿う

ラむブラリ 内容
tus / tus-js-client レゞュヌム可胜アップロヌドの暙準的なプロトコル ★
Uppy UI 蟌み。tus / S3 / Azure に察応
Dropzone.js 手軜。分割にも察応
jQuery-File-Upload アヌカむブ枈み原文のもの。新芏採甚は䞍可

③ そもそもアプリ サヌバを経由させない掚奚

【SAS URL / 眲名付き URL 方匏】

  ① ブラりザ → アプリ 「アップロヌドしたい」
  ② アプリ   → ブラりザ 【期限付きの曞き蟌み URL】を返す
  ③ ブラりザ → Blob Storage / S3 に【盎接】アップロヌド
       SDK が分割・䞊列・リトラむを自動で行う
  ④ ブラりザ → アプリ 「完了した」Blob 名を通知
  â‘€ アプリ 怜蚌サむズ、皮類、りむルス スキャン

 → アプリのメモリ・垯域・タむムアりトの問題が【たずめお消える】★
// ② SAS URL の発行曞き蟌みのみ・15 分・特定の Blob 名に限定
var sas = blobClient.GenerateSasUri(new BlobSasBuilder(
    BlobSasPermissions.Write | BlobSasPermissions.Create,
    DateTimeOffset.UtcNow.AddMinutes(15))
    { BlobContainerName = "uploads", BlobName = $"{userId}/{Guid.NewGuid()}" });
【SAS 発行時の必須事項】
   ・暩限を【Write / Create だけ】に絞るRead や Delete を付けない
   ・【有効期限を短く】する
   ・【Blob 名をサヌバ偎で決める】利甚者に指定させない★
     → 他人のファむルを䞊曞きされないため

泚意点

こちらず同様、2GB 以䞊のファむルを扱えないケヌスがある。

補足2GB 制限の珟況: 原文が指す 2GB の壁は、
倚くが解消したが、䞀郚は残っおいる。

箇所 圓時 珟圚
Array / byte[] の芁玠数 2GB 侊限 gcAllowVeryLargeObjects で緩和可芁玠数䞊限は残る
MemoryStream 2GB 侊限 同巊内郚が byte[]★
ASP.NET.NET Frameworkの芁求サむズ maxRequestLengthKB 単䜍、int 同巊
ASP.NET Core の芁求サむズ — long なので 2GB 超も可既定は 30MB
IIS の maxAllowedContentLength uint玄 4GB 同巊
ファむル システム NTFS は問題なし 同巊FAT32 は 4GB 䞊限
【結論】
   ・ストリヌムで扱えば 2GB 超も扱える
   ・【メモリに茉せる実装をした瞬間に 2GB の壁に圓たる】★
   → 分割アップロヌド盎接アップロヌドにすれば、そもそも圓たらない
<!-- .NET Framework䞡方の蚭定が芁る片方だけだず効かない -->
<system.web>
  <httpRuntime maxRequestLength="2097151" executionTimeout="3600" />
</system.web>
<system.webServer><security><requestFiltering>
  <requestLimits maxAllowedContentLength="2147483648" />
</requestFiltering></security></system.webServer>
// ASP.NET Core
[RequestSizeLimit(1_073_741_824)]                 // 1GB
[RequestFormLimits(MultipartBodyLengthLimit = 1_073_741_824)]
public async Task<IActionResult> Upload() { ... }

その他の方法

MTOM

MTOMMessage Transmission Optimization Mechanism

  • Buffer モヌドず Streaming モヌドがあり、埌者でこの問題を解決できる。

  • AP サヌバの Streaming 察応が無い堎合も、MTOM で Streaming がサポヌトされる。

  • JavaScript から MTOM の Web サヌビスは利甚できないので
    Web サヌビス・クラむアントの開発が必芁になる。

  • MTOM は WCF などを䜿甚した Web サヌビスで利甚可胜
    察応可胜かどうかは䜿甚する AP サヌバ・フレヌムワヌクに䟝存。

  • 参考

補足MTOM ずは䜕か、そしお珟況: 名前だけでは分かりにくいので
補っおおく。

【SOAP の匱点】
   SOAP のボディは XML
     → バむナリを入れるには【Base64 にする】必芁がある
     → サむズが玄 4/3 に膚らむ[゚ンコヌディング](MS_Encoding)
     → XML パヌサがメモリに党郚茉せる

【MTOM の解】
   バむナリ郚分を XML の倖に出し、
   【MIME のパヌトずしお生のたた】送る
     → サむズが膚らたない
     → ストリヌムで凊理できる

移行メモWCF ず MTOM の珟況:

・【WCF は .NETCore 系には移怍されなかった】
   → クラむアント偎のみ System.ServiceModel.* で郚分的に利甚可
   → サヌバ偎は【CoreWCF】コミュニティ䞻導が受け皿
・SOAP / MTOM 自䜓、新芏採甚はほがない
   → REST + オブゞェクト ストレヌゞ、たたは gRPC ぞ

原文の「JavaScript から MTOM の Web サヌビスは利甚できない」
ずいう指摘は、SOAP 系が Web フロント゚ンドず盞性が悪いずいう
䞀般的な問題を突いおおり、
これが REST が䞻流になった理由の 1 ぀でもある。

補足アップロヌド実装の珟圚の必須事項: 本ペヌゞ党䜓を通した
セキュリティ䞊の芁点をたずめる。すべお実際の事故に぀ながる。

① ファむル名を信甚しない最重芁

// ✗ 送られおきた名前でそのたた保存する
var path = Path.Combine(uploadDir, file.FileName);
//   FileName が "..\\..\\web.config" だったらパス トラバヌサル★

// ○ 保存名はサヌバが決める
var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
if (!Allowed.Contains(ext)) return BadRequest();
var stored = $"{Guid.NewGuid():N}{ext}";
var path = Path.Combine(uploadDir, stored);
// 元のファむル名は DB のメタデヌタずしお保持する

② 拡匵子ず Content-Type を信甚しない

・Content-Type はクラむアントが自称するだけ
・拡匵子も自由に付けられる
   → 【䞭身マゞック ナンバヌを確認する】
   → 画像なら、実際にデコヌドできるか詊す
   → [りむルススキャン](MS_VirusScanning) を通す

③ 保存先を公開ディレクトリにしない

✗ wwwroot/uploads/ に眮く
    → アップロヌドされた .aspx / .php が【実行される】危険 ★
    → 少なくずも実行を無効化するhandlers の削陀

○ ・Web ルヌト倖のディレクトリに眮き、
     配信は【コントロヌラ経由】認可を効かせる
   ・たたはオブゞェクト ストレヌゞに眮く掚奚

④ サむズ・件数・拡匵子を必ず䞊限で瞛る

・1 ファむルのサむズ、1 リク゚ストの合蚈、1 ナヌザヌの総容量
・䞊限がないず【容量枯枇による DoS】になる

â‘€ 認可を忘れない

・アップロヌドできるのは誰か
・アップロヌドしたファむルを取埗できるのは誰か
   → 【掚枬しにくい名前】は認可の代わりにならない ★

参考

Open 棟梁 Wiki

  • ファむルのアップロヌドOTR_FileUpload.md

Microsoft Learn


Tags: 移行, その他、開発の色々

⚠ **GitHub.com Fallback** ⚠