Помните и перезагружайте ввод файлов

Примечание:

В ответе ниже отражено состояние устаревших браузеров в 2009 году. Теперь вы можете установить значение элемента ввода файла через JavaScript в 2017 году.

См. ответ в этом вопросе для получения подробной информации, а также демо:
Как установить значение ввода файла программно (например: при перетаскивании файлов)?

У меня есть веб-сайт, который позволяет пользователю загружать файл несколько раз для обработки. На данный момент у меня есть один файл ввода, но я хочу, чтобы иметь возможность запомнить выбор пользователей и показать его на экране.

Что я хочу знать, так это то, что после того, как пользователь выберет файл, я запомню их выбор и повторюсь с вводом файла с файлом, предварительно выбранным при перезагрузке страницы. Все, что мне нужно знать, это запомнить и перезаписать ввод файла.

Я также открыт для подходов, которые не используют ввод файла (если это возможно).

Я использую JQuery

Ответ 1

Хорошо, вы хотите "Помнить и возвращать входные файлы", "запомнить их выбор и повторно отобразить ввод файла с предварительно выбранным файлом при перезагрузке страницы".
И в комментарии к моему предыдущему ответу вы заявляете, что на самом деле вы не открыты для альтернатив: "Извините, но нет Flash и апплетов, просто javscript и/или ввод файлов, возможно, перетащить".

Я заметил при просмотре (довольно несколько) повторяющихся вопросов (1, 2, 3 и т.д.) что практически все другие ответы соответствуют следующим: "Нет, вы не можете, это будет безопасность -issue", необязательно сопровождаемый простым концептуальным или примером кода, описывающим риск безопасности.

Однако кто-то, упрямый, как мул (не обязательно плохой до определенного уровня), может воспринимать эти ответы так: "Нет, потому что я так сказал", что действительно что-то другое: "Нет, и вот спецификации, которые разрешают это".
Итак, это моя третья и последняя попытка ответить на ваш вопрос (я направил вас к водопою, я отведу вас к реке, теперь я подталкиваю вас к источнику, но я не могу заставить вас выпить).

Изменить 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';).
Когда этот файл файл не изменился после формы-submit, Opera выведет окно безопасности, информирующее пользователя о том, какие файлы должны быть загружены в какое место (url/webserver).

Теперь можно утверждать, что все другие браузеры были нарушены спецификацией, но это было бы неправильно: поскольку спецификация заявила: "may" (он не сказал "must" ) ".. use value атрибут как начальное имя файла".
И если браузер не принимает установку значения входного файла (ака, если это значение просто "доступно только для чтения" ), браузеру также не понадобится всплывать такая "страшная" и "трудная" безопасность -pop-up (это может даже не служить цели, если пользователь этого не понял (и/или был "условным", чтобы всегда нажимать "OK" )).

Позвольте быстро перейти к HTML 5, затем..
Здесь вся эта двусмысленность очищается (но это все еще несколько озадачивает):

В 4.10.7.1.18 Состояние загрузки файла мы можем прочитать в реестре бухгалтерского учета:

  • Атрибут IDL значения находится в режиме имени файла.
    ...
  • Атрибут значения элемента должен быть опущен.

Итак, атрибут значения входного файла должен быть опущен, но он также работает в каком-то "режиме", называемом "filename", который описан в 4.10.7.4 Общие API-интерфейсы элементов ввода:

Атрибут IDL значения позволяет скриптам манипулировать значением входного элемента. Атрибут находится в одном из следующих режимов, которые определяют его поведение:

переход к этому 'имя файла режима':

При получении, он должен вернуть строку "C:\fakepath" , за которой следует имя файла первого файла в списке выбранных файлов, если таковые имеются, или пустая строка, если список пуст. При установке, если новое значение пустая строка, он должен очистить список выбранных файлов; в противном случае, он должен выдать исключение InvalidStateError.

Позвольте мне повторить: "it must вывести исключение InvalidStateError", если попытаться установить значение входного файла для строки, которая не пуста!!! (Но можно очистить поле ввода, установив его значение в пустую строку.)

Таким образом, в настоящее время и в обозримом будущем HTML5 (и в прошлом, кроме Opera) только пользователь может заполнять файл-ввод (через браузер или os-deliver 'file-chooser'). Нельзя (повторно) заполнить файл-вход в файл/каталог с помощью javascript или установить значение по умолчанию.

Получение имени файла/пути к файлу

Теперь предположим, что было бы невозможно (повторно) заполнить файл-ввод значением по умолчанию, то, очевидно, вам понадобится полный путь: directory + filename (+ extension).

В прошлом некоторые браузеры, такие как (самый известный) IE6 (до IE8), отображали полный путь + имя файла как значение: просто простой alert( elm_input_file.value ); и т.д. в javascript И браузер также отправил этот полный путь + имя файла (+ расширение) на принимающий сервер на форме-submit. Примечание. В некоторых браузерах также есть атрибут "file или fileName" (обычно отправляется на сервер), но, очевидно, это не будет включать путь.

Это реальный риск безопасности/конфиденциальности: вредоносный веб-сайт (владелец/эксплуататор) может получить путь к домашнему каталогу пользователей (где личные вещи, учетные записи, файлы cookie, пользовательская часть реестра, история, избранное, рабочий стол и т.д. находится в известных постоянных местоположениях), когда типичный нетехнический пользователь Windows загрузит свои файлы: C:\Documents and Settings\[UserName]\My Documents\My Pictures\kinky_stuff\image.ext.
Я даже не говорил о рисках при передаче данных (даже "зашифрованных" через https) или "безопасном" хранении этих данных!

Таким образом, все больше и больше альтернативных браузеров начали следовать одной из старейших проверенных мер безопасности: обмениваться информацией на основе потребности. И подавляющему большинству веб-сайтов не нужно знать путь к файлу, поэтому они отображают только имя файла (+ расширение).

К моменту выхода IE8 MS решила следить за конкуренцией и добавила параметр URLAction, называемый "Включить путь к локальному каталогу при загрузке файлов", который был настроен на "отключен" для общей интернет-зоны (и "включен" 'в доверенной зоне).

Это изменение создало небольшой хаос (в основном в "оптимизированных для IE" средах), где все виды как пользовательского кода, так и проприетарные "элементы управления" не могли получить имя файла загруженных файлов: они были жестко запрограммированы строка, содержащая полный путь и извлечение части после последней обратной косой черты (или слэш, если вам повезло...). 1, 2

Вперед появился HTML5,
и, как вы читали выше, "имя файла режима" указывает:

При получении он должен вернуть строку "C:\fakepath" , а затем имя файла первого файла в списке выбранных файлов, если таковые имеются, или пустую строку, если список пуст.

и они отмечают, что

Это требование "фальшивого пути" является печальной случайностью истории

и

По историческим причинам атрибут IDL значения префикс filename со строкой "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
}

Примечание. Я думаю, что эта функция глупа: вся задача состоит в том, чтобы всегда иметь поддельный путь к окну. Итак, первое "если" не только бесполезно, но даже вызывает ошибку: представьте себе пользователя с более старым браузером, который загружает файл из: 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).

Пусть recap (получение имени пути/файла):

  • более старые браузеры (и более новые браузеры, где можно включить это как вариант, например IE >= 8), откроют полный путь к файлам windows/unix.
  • менее старые браузеры не будут раскрывать какой-либо путь, просто имя файла (+ расширение)
  • текущие/будущие/HTML5-совместимые браузеры всегда будут предварительно подставлять строку: c:\fakepath\ в имя файла при получении значения входного файла
    Кроме того, они вернут только первое имя файла (из "списка выбранных файлов" ), если файл-вход принимает несколько файлов, а пользователь выбрал несколько файлов.

Таким образом, в недавнем прошлом, в настоящее время и в предсказуемом будущем HTML5 обычно будет только имя файла.

Это приводит нас к тому, что нам нужно изучить: этот "список выбранных файлов" /несколько файлов, что приводит нас к третьей части головоломки:

(HTML5) API файлов

Прежде всего: "File API" не следует путать с "API файловой системы, здесь представлен реферат файла Системный API:

Эта спецификация определяет API для навигации по иерархиям файловых систем и определяет средство, с помощью которого пользовательский агент может выставлять изолированные области локальной файловой системы пользователя в веб-приложения. Он основан на [FILE-WRITER-ED], который, в свою очередь, построен на [FILE-API-ED], каждый из которых добавляет разные функции.

"изолированные области пользовательской локальной файловой системы" уже ясно указывают, что нельзя использовать это для захвата пользовательских файлов вне песочницы (поэтому это не относится к вопросу, хотя можно было скопировать пользовательские файлы, выбранный файл в постоянное локальное хранилище и повторно загружать эту копию с использованием AJAX и т.д. Полезно как "повторить попытку" при неудачной загрузке. Но это не было бы указателем на исходный файл, который мог бы быть изменен в среднем).
Еще более важным является тот факт, что только webkit (думаю, более старые версии chrome) реализовал эту функцию, и спецификация, скорее всего, не выживет, поскольку она is no more actively maintained, the specification is abandonned for the moment as it didn't get any significant traction

Продолжайте работу с File API,
он абстрактный говорит нам:

Эта спецификация предоставляет API для представления файловых объектов в веб-приложений, а также программным выбором их и доступ к их данным. Это включает в себя:

  • Интерфейс FileList, который представляет собой массив отдельных выбранных файлов из базовой системы. Пользовательский интерфейс для выбор может быть вызван через <input type="file">, то есть когда вход элемент находится в состоянии загрузки файла [HTML].
  • Интерфейс Blob, который представляет неизменяемые необработанные двоичные данные и позволяет получить доступ к диапазонам байтов внутри объекта Blob как отдельному Blob.
  • Интерфейс файлов, который включает в себя только точные информационные атрибуты о файле, такие как его имя и дату последней модификации (на диске) файла.
  • Интерфейс FileReader, который предоставляет методы для чтения файла или Blob и модель события для получения результатов этих чтений.
  • Схема URL для использования с двоичными данными, такими как файлы, чтобы они могли ссылаться в веб-приложениях.

Итак, FileList может быть заполнено полем ввода в файловом режиме: <input type="file">.
Это означает, что все вышеперечисленное об атрибуте value все еще применяется!

Когда поле ввода находится в файловом режиме, оно получает атрибут только для чтения files, который представляет собой массив, подобный FileList object, который ссылается на выбранный пользователем файл ввода и является (/are) доступным для FileList interface.
Я упоминал, что атрибут files типа FileList имеет значение только для чтения (File API, раздел 5.2 )?

Интерфейс HTMLInputElement [HTML] имеет атрибут readonly типа FileList...

Ну, а как насчет перетаскивания?

Из mdn-documentation - выбор файлов с использованием перетаскивания

Реальная магия происходит в функции drop():

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

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

  handleFiles(files);
}

Здесь мы извлекаем поле dataTransfer из события, затем вытягиваем список файлов из него, передавая это handleFiles(). С этого момента on, обработка файлов одинакова, независимо от того, использовал ли пользователь вход элемент или перетаскивание.

Итак, (как и поле ввода type="file"), атрибут dataTransfer имеет атрибут типа массива files, который является массивом типа FileList object, и мы только что узнали (выше), что FileList только для чтения..

FileList содержит ссылки на файлы (ы), которые пользователь выбрал (или сбросил на целевую точку) и некоторые атрибуты. Из File API Section 7.2 Атрибуты файлов мы можем прочитать:

имя

Имя файла; при получении, это должно вернуть имя файл как строку. Существует множество вариантов имени файла для разных системы; это просто имя файла, без пути Информация. При получении, если пользовательские агенты не могут сделать эту информацию доступны, они должны возвращать пустую строку.

lastModifiedDate

Последняя измененная дата файла. При получении, если пользовательские агенты могут сделайте эту информацию доступной, она должна вернуть новую дату [HTML] объект инициализирован до последней измененной даты файла. Если последний дата и время модификации неизвестны, атрибут должен возвращаться текущую дату и время как объект Date.

и есть атрибут size:

F.size - это то же самое, что и размер аргумента Blob файлаBbob, который должен быть неизменными необработанными данными F.

Опять нет пути, просто имя файла только для чтения.

Таким образом:

  • (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 - это имя файла для выбранного первого файла (и это value, который возвращается из input type="file").

Как насчет этой "схемы URL для использования с двоичными данными, такими как файлы, чтобы они могли ссылаться в веб-приложениях", безусловно, может содержать личную ссылку на файл, который выбран пользователем?

Из File API - URL для ссылки на Blob и File можно узнать, что:

Эта спецификация определяет схему с URL-адресами сортировки:
блоб:. 550e8400-e29b-41d4-a716-446655440000 # aboutABBA

Они хранятся в URL store (и браузеры должны иметь собственный мини-HTTP-сервер на борту, поэтому вы можете использовать эти URL-адреса в css, img src и даже XMLHttpRequest.

Можно создать те Blob URL с помощью:

  • var myBlobURL=window.URL.createFor(object); возвращает Blob URL, который автоматически отменяется после его первого использования.
  • var myBlobURL=window.URL.createObjectURL(object, flag_oneTimeOnly); возвращает повторно используемый Blob URL (если флаг_oneTImeOnly не имеет значения true) и может быть отменен с помощью window.URL.revokeObjectURL(myBlobURL).

Бинго, вы можете подумать... однако... URL store поддерживается только во время сеанса (так что он выживет при обновлении страницы, так как это все тот же сеанс) и потерян, когда документ выгружен.

Из MDN - Использование URL-адресов объектов:

URL-адрес объекта - это строка, идентифицирующая объект File. Каждый раз, когда вы call window.URL.createObjectURL(), создается уникальный URL-адрес объекта, даже если вы уже создали URL-адрес объекта для этого файла. Каждый из они должны быть освобождены. Хотя они автоматически освобождаются, когда документ выгружается, если ваша страница использует их динамически, вы должны освободить их явно, вызвав window.URL.revokeObjectURL()

Это означает, что даже если вы храните строку Blob URL в 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: окончательное руководство - Дэвид Фланаган, глава 22: Файловая система api
Как сохранить результат window.URL.createObjectURL() для будущего использования?
Как долго сохраняется Blob?
Как разрешить C:\fakepath?

Ответ 2

Создайте поле ввода в своей форме. Когда пользователь выбирает файл, скопируйте результат в это поле, например:

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

Собственно, результат не копируется точно, вместо этого он копирует "C:/fakepath/SELECTED_FILE_NAME". Хотя вам не разрешено устанавливать значение ввода файла, вы можете установить значение поля ввода текста без "C:/fakepath/", когда сервер подготавливает форму.

Теперь, когда сервер получит форму обратно, проверьте поле ввода текста. Если он начинается с "C:/fakepath/", пользователь должен выбрать новый файл, поэтому загрузите свой новый выбор. Если это не так, то пользователь выбрал предыдущий выбор, который не должен быть проблемой, поскольку, согласно оригинальному вопросу, предыдущий выбор был загружен ранее и СЛЕДУЕТ (по крайней мере, с соответствующим программированием, он МОЖЕТ) на сервере.

Ответ 3

рискуя наступить на пальцы огромной информации, предоставленной GitaarLAB, я мог бы предположить, что DaveWalley очень близко обеспечивает практическое решение проблемы. Вы оба очень помогли мне, спасибо.

Мое сообщение о возврате домой,

  • Вход файла предлагает одностороннюю улицу. Он сидит там, ожидая обработки следующей загрузки. Это все, что он делает, в значительной степени. Он получает.
  • Текст для ввода текста в формате div или только для чтения, расположенный рядом с меткой для ввода файла, может выполнять показ. то есть его можно использовать для указания пользователю: "Вот что загружено в текущий момент:" - имя файла, которое должно отображаться, должно быть заполнено из вашей логики на стороне сервера.

Таким образом, основной пример использования:

  • пользователь загружает файл через вход формы на веб-странице
  • Серверная логика хранит файл
  • В цикле Ajax или перезагрузке веб-страницы серверный код идентифицирует строку fileName для записи в div, "Здесь, что в настоящее время загружено"
  • Ввод файла также может быть повторно представлен, так что пользователь может также загрузить другой файл. Вместо этого.

Возможно, вам понадобится сделать еще одну работу, чтобы обрабатывать "* обязательную" проверку, но эту другую историю.

Ответ 4

Если все, что вы действительно хотите, это временная настойчивость к данным файла, например, если на странице есть ошибка или если вы хотите иметь страницу подтверждения, прежде чем передавать данные в базу данных, то это обычно выполняются с временным размещением файлов и хранением данных в скрытых полях.

Это не заполняет поле ввода файла. Но вы также можете просто взять имя введенного файла и поместить его рядом с полем ввода файла.

так:

<input type=hidden name="filename" value="<?php echo $filename; ?>" />
<input type="file" name="uploadfile" size="50" />

<?php if (!empty($filename)) echo $filename; ?>