web-dev-qa-db-ja.com

ファイル入力を記憶して再入力する

注:

以下の回答は、2009年のレガシーブラウザの状態を反映しています。2017年にJavaScriptを介してファイル入力要素の値を実際に設定できるようになりました。

詳細およびデモについては、この質問の回答を参照してください。
ファイルの入力値をプログラムで設定する方法(つまり、ファイルをドラッグアンドドロップするとき)?

ユーザーが処理のためにファイルを複数回アップロードできるWebサイトがあります。現時点では、単一のファイル入力がありますが、ユーザーの選択を記憶して、画面に表示できるようにしたいと考えています。

ユーザーがファイルを選択した後の方法を知りたいのは、選択内容を記憶し、ページのリロード時に事前に選択したファイルでファイル入力を再表示することです。知っておく必要があるのは、ファイル入力を記憶して再入力する方法だけです。

また、ファイル入力を使用しないアプローチも可能です(可能な場合)。

私はJQueryを使用しています

42
user1150103

OK、「ファイル入力を記憶して再入力」、「選択を記憶して、ファイル入力を再表示してページのリロード時に事前選択されたファイル "..
そして、以前の回答へのコメントで、あなたは実際に選択肢に開かれていないと述べています:「申し訳ありませんが、フラッシュとアプレットはありません。javscriptおよび/またはファイル入力、おそらくドラッグアンドドロップです。」

ブラウジング中に(かなり)重複した質問に気づいた 123 など)、実質的に他のすべての答えは「できません、それはセキュリティの問題です」という線に沿っており、オプションでセキュリティリスクの概要を示す簡単な概念またはコード例が続きます。

ただし、ラバとして頑固な人(特定のレベルまでは必ずしも悪いことではない)は、「いいえ、そう言ったので」と答える場合があります。それを許可しない」。
これが、あなたの質問に答えようとする3回目の最後の試みです(私はあなたを水飲み場に案内し、あなたを川に導きます、今私はあなたを源に押していますが、私はできません飲ませる)。

編集3:

あなたがしたいことは、実際にはかつて RFC1867 セクション3.4で説明/「提案」されていました:

VALUE属性は、デフォルトのファイル名の<INPUT TYPE=file>タグとともに使用される場合があります。この使用はおそらくプラットフォームに依存します。ただし、複数のトランザクションのシーケンスでは、たとえば、同じファイル名を何度も繰り返し入力するようにユーザーに要求するのを避けるのに役立ちます。

実際、HTML 4.01仕様セクション17.4.1は次のことを指定しています。

ユーザーエージェントは、初期ファイル名としてvalue属性の値を使用できます。

(「ユーザーエージェント」とは「ブラウザ」を意味します)。

Javascriptはフォーム(ファイル入力を含む)の変更と送信の両方が可能であり、cssを使用してフォーム/フォーム要素(ファイル入力など)を隠すことができるという事実を考えると、上記のステートメントだけでサイレントアップロードが可能ユーザーの意図/知識のないユーザーのコンピューターからのファイル。
これが不可能であることは明らかに非常に重要であり、そのため、(上記)RFC1867はセクション8セキュリティの考慮事項で述べています。

ユーザーエージェントが、ユーザーが送信を明示的に要求していないファイルを送信しないことが重要です。したがって、HTML解釈エージェントは、<INPUT TYPE=file VALUE="yyyy">で提案される可能性のあるデフォルトのファイル名を確認することが期待されます。

ただし、この機能を実装したブラウザー(私が知っている)は、(一部の古いバージョン)Operaのみでした:<input type="file" value="C:\foo\bar.txt>または値の設定javascript(Elm_input_file.value='c:\\foo\\bar.txt';)。
form-submitでこのファイルボックスが変更されなかった場合、Operaは、どのファイルにアップロードしようとしているのかをユーザーに通知するセキュリティウィンドウをポップアップします場所(url/webserver)。

今、他のすべてのブラウザは仕様に違反していると主張するかもしれませんが、それは間違っているでしょう:仕様には「may」(それはnotsay "must") "..初期ファイル名としてvalue属性を使用"。
そして、ブラウザがファイル入力値の設定を受け入れない場合(別名、その値は単に「読み取り専用」になります)、ブラウザはそのような「怖い」ポップアップを表示する必要もありません。そして、「難しい」セキュリティポップアップ(ユーザーがそれを理解していなかった場合(および/または常に「OK」をクリックするように「条件付けられていた」場合)、目的にかなわないこともあります)。

それでは、HTML 5に早送りしましょう。
ここで、このすべての曖昧さが解消されます(それでもなお、いくつかの不可解な点があります)。

4.10.7.1.18 File Upload stateで、簿記の詳細を読むことができます

  • 値IDL属性はモードファイル名にあります。
    ...
  • 要素の値属性は省略しなければなりません。

したがって、ファイル入力の値属性は省略する必要がありますが、4.10.7.4共通入力要素APIで説明されている「ファイル名」と呼ばれる何らかの「モード」でも動作します

値IDL属性を使用すると、スクリプトで入力要素の値を操作できます。属性は、その動作を定義する次のモードのいずれかにあります。

この「 mode filename 」にスキップ:

取得時に、文字列「C:\ fakepath \」に続いて、選択されたファイルのリスト内の最初のファイルのファイル名(存在する場合)、またはリストが空の場合は空の文字列を返す必要があります。設定時に、新しい値が空の文字列である場合、選択したファイルのリストを空にする必要があります。それ以外の場合は、InvalidStateError例外をスローする必要があります。

それを繰り返してみましょう:ファイル入力値を空でない文字列に設定しようとすると、「it must throwsInvalidStateErrorexception」!!! (ただし、値を空の文字列に設定することにより、入力フィールドをクリアできます。)

したがって、現在および予見可能なHTML5の将来(および過去、Operaを除く)、ユーザーのみがfile-inputに入力します(ブラウザまたはOS提供の「file-chooser」経由)。 javascriptを使用して、またはデフォルト値を設定して、ファイル/ディレクトリへのファイル入力を(再)入力することはできません。

ファイル名/ファイルパスの取得

さて、仮定ファイル入力にデフォルト値を(再-)入力することは不可能ではなかったので、明らかにフルパスが必要です:ディレクトリ+ファイル名(+拡張子)。

過去には、(最も注目すべき)IE6(IE8まで)didなどの一部のブラウザーは、値としてフルパス+ファイル名を明らかにします。単なるalert( Elm_input_file.value );などです。 javascriptおよびブラウザは、この完全なパス+ファイル名(+拡張子)をフォーム送信で受信サーバーに送信しました。
注:一部のブラウザには「ファイルまたはファイル名」属性(通常はサーバーに送信される)もありますが、明らかにこれにはパスが含まれません。

これは現実的なセキュリティ/プライバシーリスクです。悪意のあるWebサイト(所有者/悪用者)は、ユーザーのホームディレクトリ(個人のもの、アカウント、Cookie、レジストリのユーザー部分、履歴、お気に入り、デスクトップなど)へのパスを取得できます通常の非技術的なWindowsユーザーがC:\Documents and Settings\[UserName]\My Documents\My Pictures\kinky_stuff\image.extからファイルをアップロードするとき、既知の一定の場所にあります。
データを送信する際のリスク(httpsを介して「暗号化」される場合)やこのデータの「安全な」ストレージについても説明しませんでした

そのため、最も古い実績のあるセキュリティ対策の1つである、知る必要性に基づいて情報を共有する代替ブラウザがますます増えています。
そして、ほとんどのウェブサイトはファイルパスを知る必要はありません。したがって、ファイル名(+拡張子)のみを明らかにしました。

IE8がリリースされるまでに、MSは競争に従うことを決定し、「ファイルをアップロードするときにローカルディレクトリパスを含める」と呼ばれるURLActionオプションを追加しました。これは、一般的なインターネットゾーンに対して「無効」(および「有効」に設定)デフォルトでは信頼できるゾーン)。

この変更により、(主に「IE用に最適化された」環境で)小さな混乱が発生し、あらゆる種類のカスタムコードと独自の「コントロール」の両方がアップロードされたファイルのファイル名を取得できませんでした。完全なパスで、最後のバックスラッシュ(またはラッキーだった場合はスラッシュ)の後の部分を抽出します。 12

HTML5に沿って、
そしてあなたが上記で読んだように、「モードファイル名」は以下を指定します:

取得時に、文字列「C:\ fakepath \」に続いて、選択されたファイルのリスト内の最初のファイルのファイル名(存在する場合)、またはリストが空の場合は空の文字列を返す必要があります。

そして彼らはそれに注意する

この「フェイクパス」要件は、歴史の悲しい事故です

そして

歴史的な理由から、値IDL属性はファイル名の前に文字列「C:\ fakepath \」を付けます。一部のレガシーユーザーエージェントには、実際にはフルパスが含まれていました(これはセキュリティ上の脆弱性でした)。この結果、後方互換性のある方法で値IDL属性からファイル名を取得するのは簡単ではありません。次の関数は、適切な互換性のある方法でファイル名を抽出します。

function extractFilename(path) {
  if (path.substr(0, 12) == "C:\\fakepath\\")
    return path.substr(12); // modern browser
  var x;
  x = path.lastIndexOf('/');
  if (x >= 0) // Unix-based path
    return path.substr(x+1);
  x = path.lastIndexOf('\\');
  if (x >= 0) // Windows-based path
    return path.substr(x+1);
  return path; // just the filename
}

注:私はこの関数は愚かだと思う:全体のポイントは常に解析するための偽のWindowsパスを持っていることです。したがって、最初の「if」は役に立たないだけでなく、バ​​グを招くことさえあります。からのファイル:c:\fakepath\Some folder\file.ext(戻り値:Some folder\file.ext)...
単に使用します:

function extractFilename(s){ 
  // returns string containing everything from the end of the string 
  //   that is not a back/forward slash or an empty string on error
  //   so one can check if return_value===''
  return (typeof s==='string' && (s=s.match(/[^\\\/]+$/)) && s[0]) || '';
} 

(HTML5仕様が明確に意図したとおり)。

要約しましょう(パス/ファイル名を取得):

  • 古いブラウザ(およびIE> = 8のようなオプションとしてこれを有効にできる新しいブラウザ)は、完全なwindows/unixパスを明らかにします
  • 古いブラウザではパスは表示されず、ファイル名(拡張子)のみが表示されます
  • current/future/HTML5準拠のブラウザは、ファイル入力のvalueを取得するときに、常にファイル名の前にc:\fakepath\という文字列を付加します。
    さらに、ファイル入力が複数のファイルとユーザーを受け入れる場合、firstファイル名(「選択されたファイルのリスト」から)のみを返します。複数のファイルを選択しました。

したがって、最近の過去、現在、および予見可能なHTML5の将来では、通常はファイル名のみを取得します。

これで最後に調べる必要があるのは、この「選択したファイルのリスト」/複数ファイルです。これにより、パズルの3番目の部分に進みます。

(HTML5)ファイルAPI

まず、「ファイルAPI」と「 ファイルシステムAPI 」を混同しないでください。ファイルシステムAPIの概要は次のとおりです。

この仕様は、ファイルシステム階層をナビゲートするためのAPIを定義し、ユーザーエージェントがユーザーのローカルファイルシステムのサンドボックスセクションをWebアプリケーションに公開する手段を定義します。 [FILE-WRITER-ED]に基づいて構築され、[FILE-API-ED]に基づいて構築され、それぞれが異なる種類の機能を追加します。

「ユーザーのローカルファイルシステムのサンドボックス化されたセクション」は、これを使用してサンドボックスの外にあるユーザーファイルを取得できないことを既に明確に示しています(そのため、1つユーザーが選択したファイルを永続的なローカルストレージにコピーし、AJAXなどを使用してcopyを再アップロードします。失敗したアップロードで「再試行」します。しかし、その間に変更された可能性のある元のファイルへのポインタではありません)。
さらに重要なのは、Webkit(古いバージョンのchromeを考えてください)のみがこの機能を実装しているという事実であり、仕様はおそらく生き残れません is no more actively maintained, the specification is abandonned for the moment as it didn't get any significant traction

File API 」に進みましょう。
それは抽象的です:

この仕様は、Webアプリケーションでファイルオブジェクトを表すためのAPIを提供し、プログラムでそれらを選択してデータにアクセスします。これも:

  • 基になるシステムから個別に選択されたファイルの配列を表すFileListインターフェイス。選択用のユーザーインターフェイスは、<input type="file">を介して、つまり入力要素がファイルアップロード状態[HTML]にあるときに呼び出すことができます。
  • 不変の生のバイナリデータを表すBlobインターフェイス。Blobオブジェクト内のバイト範囲に個別のBlobとしてアクセスできます。
  • Fileインターフェイス。ファイルの名前やファイルの(ディスク上の)最終変更日など、ファイルに関する読み取り専用の情報属性が含まれます。
  • FileReaderインターフェイス。ファイルまたはBlobを読み取るメソッドと、これらの読み取りの結果を取得するイベントモデルを提供します。
  • Webアプリケーション内で参照できるように、ファイルなどのバイナリデータで使用するURLスキーム。

そのため、ファイルモードの入力フィールド<input type="file">FileListを設定できます。
それは、値属性に関する上記のすべてがまだ適用されることを意味します!

入力フィールドがファイルモードの場合、 read-only属性files これは、入力要素のユーザーが選択したファイルを参照し、FileList objectによってアクセス可能な配列のようなFileList interfaceです。
files型のFileList属性はread-only (ファイルAPIセクション5.2) ? :

HTMLInputElementインターフェイス[HTML]には、FileList型の読み取り専用属性があります...

さて、ドラッグアンドドロップはどうですか?

mdn-documentationから-ドラッグアンドドロップを使用したファイルの選択

本当の魔法はdrop()関数で起こります:

function drop(e) {
  e.stopPropagation();
  e.preventDefault();

  var dt = e.dataTransfer;
  var files = dt.files;

  handleFiles(files);
}

ここでは、イベントからdataTransferフィールドを取得し、そこからファイルリストを引き出して、それをhandleFiles()に渡します。この時点から、ユーザーが入力要素を使用した場合でも、ドラッグアンドドロップを使用した場合でも、ファイルの処理は同じです。

そのため、(input-field type = "file"と同様に)イベントのdataTransfer属性には配列のような属性filesがあり、これは配列のようなFileList objectです。 FileListがread-onlyであることを(上記で)学習しました。

FileListには、ユーザーが選択した(またはドロップターゲットにドロップした)ファイルへの参照といくつかの属性が含まれます。 File API Section 7.2 File Attributes から次を読み取ることができます。

ファイルの名前。取得時に、これはファイル名を文字列として返す必要があります。さまざまなシステムでさまざまなファイル名のバリエーションがあります。これは、パス情報なしの単なるファイル名です。取得時に、ユーザーエージェントがこの情報を利用できない場合、空の文字列を返す必要があります。

lastModifiedDate

ファイルの最終変更日。取得時に、ユーザーエージェントがこの情報を利用できる場合、これはファイルの最終変更日付に初期化された新しいDate [HTML]オブジェクトを返す必要があります。最終変更日時が不明な場合、属性は現在の日時をDateオブジェクトとして返す必要があります。

size属性があります:

F.sizeはfileBits Blob引数のサイズと同じです。これはFの不変の生データでなければなりません。

繰り返しますが、パスはなく、read-onlyファイル名のみです。

したがって:

  • (Elm_input||event.dataTransfer).filesはFileListオブジェクトを提供します。
  • (Elm_input||event.dataTransfer).files.lengthはファイルの数を示します。
  • (Elm_input||event.dataTransfer).files[0]は最初に選択されたファイルです。
  • (Elm_input||event.dataTransfer).files[0].nameは、選択された最初のファイルのファイル名です
    (これは、入力type = "file"から返されるvalueです)。

この「Webアプリケーション内で参照できるように、ファイルなどのバイナリデータで使用するURLスキーム」についてはどうでしょうか。ユーザーが選択したファイルへのプライベート参照を確実に保持できますか。

File API-BlobおよびFile参照のURL から、次のことがわかります。

この仕様では、次の種類のURLを含むスキームを定義しています。
blob:550e8400-e29b-41d4-a716-446655440000#aboutABBA。

これらはURL storeに格納されます(ブラウザにはcss、img src、さらにXMLHttpRequestでもこれらのURLを使用できるように、独自のミニHTTPサーバーが必要です。

これらのBlob URLsは次のようにして作成できます:

  • var myBlobURL=window.URL.createFor(object);は、最初の使用後に自動的に取り消されるBlob URLを返します。
  • var myBlobURL=window.URL.createObjectURL(object, flag_oneTimeOnly);は再利用可能なBlob URLを返し(flag_oneTImeOnlyがtrueと評価されない限り)、window.URL.revokeObjectURL(myBlobURL)で取り消すことができます。

ビンゴはあなたが思うかもしれません...しかし... URL Storeはセッション中にのみ維持され(そのため、同じセッションであるためページ更新後も存続します)、ドキュメントがアンロードされると失われます。

MDNから-オブジェクトURLの使用

オブジェクトURLは、Fileオブジェクトを識別する文字列です。 window.URL.createObjectURL()を呼び出すたびに、そのファイルのオブジェクトURLが既に作成されている場合でも、一意のオブジェクトURLが作成されます。これらはそれぞれリリースする必要があります。ドキュメントがアンロードされると自動的に解放されますが、ページで動的に使用される場合は、window.URL.revokeObjectURL()を呼び出して明示的に解放する必要があります

つまり、Blob URLstringをCookieまたは永続的なローカルストレージに格納しても、その文字列は新しいセッションでは役に立たないということです。

これで、完全な円と最終的な結論が得られます。
入力フィールドまたはユーザーが選択したファイルを(再)入力することはできません 「ローカルストレージ」エリア)。
(ユーザーに古いバージョンのOperaの使用を強制するか、ユーザーにIEと一部のactiveXコーディング/モジュール(カスタムファイルピッカーの実装)の使用を強制する場合を除き、等)

さらにいくつかの読書:
http://www.cs.tut.fi/~jkorpela/forms/file.html
https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
http://www.html5rocks.com/en/tutorials/file/filesystem/
http://www.html5rocks.com/en/tutorials/file/dndfiles/
http://caniuse.com/filereader
JavaScript:決定版ガイド-David Flanagan、第22章:ファイルシステムAPI
将来の使用のためにwindow.URL.createObjectURL()の結果を保存する方法は?
Blobはどれくらいの期間持続しますか?
C:\ fakepathの解決方法

205
GitaarLAB

フォームに入力フィールドを作成します。ユーザーがファイルを選択したら、次のような結果をこのフィールドにコピーします。

jQuery('#inFile').change(
 function(){ jQuery('#inCopy').val( jQuery('#inFile').val() ); }
);

実際、結果は正確にはコピーされず、代わりに「C:/ fakepath/SELECTED_FILE_NAME」がコピーされます。ファイル入力の値を設定することはできませんが、サーバーがフォームを準備するときに、「C:/ fakepath /」なしでテキスト入力フィールドの値を設定できます。

サーバーがフォームを取り戻したら、テキスト入力フィールドを確認します。 「C:/ fakepath /」で始まる場合、ユーザーは新しいファイルを選択している必要があるため、新しい選択をアップロードします。そうでない場合、ユーザーは以前の選択を選択しましたが、元の質問によると、以前の選択が以前にアップロードされ、SHOULD(少なくとも適切なプログラミングでは、COULD)サーバー上。

1
DaveWalley

GitaarLABが提供する膨大な情報に足を踏み入れる危険性があるので、DaveWalleyが問題の実用的な解決策を提供していることを示唆するかもしれません。二人ともとても助けてくれたので、ありがとう。

私の持ち帰りのメッセージは、

  1. ファイル入力は一方通行です。そこに座って次のアップロードを処理します。それは、それがすることのほとんどです。それ受信
  2. ファイル入力のラベルの近くにあるdivまたは読み取り専用のテキストフォーム入力は、servingを実行できます。つまり、「現在アップロードされているものは次のとおりです」ということをユーザーに伝えるために使用できます。-表示するファイル名は、サーバー側のロジックから入力する必要があります。

したがって、基本的な使用例は次のとおりです。

  • ユーザーがWebページのフォーム入力を介してファイルをアップロードする
  • サーバー側のロジックはファイルを保存します
  • AjaxサイクルまたはWebページのリロードで、サーバー側のコードがfileName文字列を識別してdivに書き込む、「これは現在アップロードされているものです」
  • ファイル入力も再表示されるため、ユーザーは別のファイルをアップロードすることもできます/代わりに。

「*必須」検証を処理するには、さらに作業が必要になる場合がありますが、それは別の話です。

0
owenmck