Запомните и заново заполните ввод файла
Замечания:
Приведенные ниже ответы отражают состояние устаревших браузеров в 2009 году. Теперь вы можете фактически установить значение элемента ввода файла через JavaScript в 2017 году.
Подробности см. В ответе на этот вопрос, а также в демоверсии:
Как установить значение входного файла программно (например, при перетаскивании файлов)?
У меня есть веб-сайт, который позволяет пользователю загружать файл несколько раз для обработки. На данный момент у меня есть один файл ввода, но я хочу, чтобы иметь возможность запомнить выбор пользователей и показать его на экране.
То, что я хочу знать, как это сделать, - после того, как пользователь выберет файл, я запомню его выбор и снова выведу на экран ввод файла с файлом, предварительно выбранным при перезагрузке страницы. Все, что мне нужно знать, это как запомнить и снова заполнить ввод файла.
Я также открыт для подходов, которые не используют ввод файла (если это возможно).
Я использую JQuery
4 ответа
Хорошо, вы хотите "запомнить и повторно заполнить ввод файла ", "запомнить их выбор и заново отобразить ввод файла с файлом, предварительно выбранным при перезагрузке страницы "..
И в комментарии к моему предыдущему ответу вы утверждаете, что на самом деле вы не открыты для альтернатив: "Извините, но нет Flash и апплетов, просто ввод javscript и / или файлов, возможно перетаскивание".
Я заметил, просматривая (довольно некоторые) повторяющиеся вопросы ( 1, 2, 3 и т. Д.), Что практически все остальные ответы имеют вид: "Нет, вы не можете, это было бы проблемой безопасности", опционально следовал простым концептуальным примером или примером кода, описывающим угрозу безопасности.
Тем не менее, кто-то упрямый как мул (не обязательно плохой до определенного уровня) может воспринимать эти ответы как: "Нет, потому что я так сказал", что на самом деле совсем не то: "Нет, и вот спецификации, которые запретить это ".
Итак, это моя третья и последняя попытка ответить на ваш вопрос (я привел вас к водопою, я веду вас к реке, теперь я подталкиваю вас к источнику, но я не могу заставить вас пить).
Изменить 3:
То, что вы хотите сделать, было фактически когда-то описано / "предложено" в Разделе 3.4 RFC1867:
Атрибут 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';
).
Когда это поле файла не изменялось при отправке формы, Opera открывала окно безопасности, информирующее пользователя о том, какие файлы (где) должны быть загружены в какое место (URL / веб-сервер).
Теперь можно утверждать, что все другие браузеры нарушали спецификацию, но это было бы неправильно: так как в спецификации говорилось: " may
"(не сказал" must
")".. использовать атрибут value в качестве начального имени файла ".
И, если браузер не принимает установку значения ввода файла (иначе говоря, это значение должно быть "только для чтения"), тогда браузеру также не нужно будет показывать такую "страшную" и "сложную" безопасность - всплывающее окно (которое может даже не соответствовать своей цели, если пользователь этого не понимает (и / или было "обусловлено", чтобы всегда нажимать "ОК")).
Давайте теперь перенесемся в HTML 5..
Здесь вся эта двусмысленность прояснена (все же это все еще требует некоторой загадки):
В разделе 4.10.7.1.18 Состояние загрузки файла мы можем прочитать подробности бухгалтерии:
- Атрибут значения IDL находится в режиме имени файла.
...- Атрибут значения элемента должен быть опущен.
Таким образом, атрибут value файлового ввода должен быть опущен, но он также работает в каком-то "режиме", называемом "filename", который описан в 4.10.7.4 API-интерфейсы общих элементов ввода:
Атрибут value IDL позволяет сценариям манипулировать значением элемента ввода. Атрибут находится в одном из следующих режимов, которые определяют его поведение:
переход к этому " имени файла режима ":
При получении он должен вернуть строку "C:\fakepath\", за которой следует имя файла первого файла в списке выбранных файлов, если таковые имеются, или пустую строку, если список пуст. При установке, если новое значение является пустой строкой, оно должно очистить список выбранных файлов; в противном случае он должен вызвать исключение InvalidStateError.
Позвольте мне повторить это: "это must
генерировать исключение InvalidStateError ", если вы пытаетесь установить значение ввода файла в строку, которая не является пустой!!! (Но можно очистить поле ввода, установив его значение в пустую строку.)
Таким образом, в настоящее время и в обозримом будущем HTML5 (и в прошлом, за исключением Opera), только пользователь может заполнять ввод файла (через браузер или предоставляемый os 'file-chooser'). Нельзя (повторно) заполнить файл ввода в файл / каталог с помощью JavaScript или путем установки значения по умолчанию.
Получение имени файла / пути к файлу
Теперь предположим, что было невозможно (повторно) заполнить файл ввода значением по умолчанию, тогда, очевидно, вам потребуется полный путь: каталог + имя файла (+ расширение).
В прошлом некоторые браузеры, такие как (наиболее заметный) IE6 (до IE8) , отображали полный путь + имя файла в качестве значения: просто alert( elm_input_file.value );
и т.д. в JavaScript и браузер также отправил этот полный путь + имя файла (+ расширение) на принимающий сервер при отправке формы.
Примечание: некоторые браузеры также имеют атрибут "file or 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 значения ставит перед именем файла строку "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
- менее старые браузеры не будут раскрывать пути, только имя файла (+ расширение)
- текущие / будущие /HTML5-совместимые браузеры всегда будут предварительно ожидать строку:
c:\fakepath\
к имени файла при получении значения входного файла
Кроме того, они будут возвращать только первое имя файла (из "списка выбранных файлов"), если при вводе файла будет принято несколько файлов, а пользователь выбрал несколько файлов.
Таким образом, в недавнем прошлом, в настоящее время и в обозримом будущем HTML5 каждый обычно получает только имя файла.
Это подводит нас к последнему пункту, который нам нужно изучить: это "список выбранных файлов" / нескольких файлов, который приводит нас к третьей части головоломки:
(HTML5) Файловый API
Прежде всего: "Файловый 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
Давайте продолжим с " Файловым API ",
это резюме говорит нам:
Эта спецификация предоставляет API для представления файловых объектов в веб-приложениях, а также для их программного выбора и доступа к их данным. Это включает:
- Интерфейс FileList, представляющий массив индивидуально выбранных файлов из базовой системы. Пользовательский интерфейс для выбора может быть вызван через
<input type="file">
, т. е. когда элемент ввода находится в состоянии загрузки файла [HTML] .- Интерфейс Blob, который представляет неизменяемые необработанные двоичные данные и обеспечивает доступ к диапазонам байтов в объекте Blob в качестве отдельного BLOB-объекта.
- Файловый интерфейс, который включает в себя только для чтения информационные атрибуты о файле, такие как его имя и дата последней модификации (на диске) файла.
- Интерфейс FileReader, который предоставляет методы для чтения файла или большого двоичного объекта, а также модель событий для получения результатов этих чтений.
- Схема URL для использования с двоичными данными, такими как файлы, чтобы на них можно было ссылаться в веб-приложениях.
Так, FileList
может быть заполнено полем ввода в file-mode: <input type="file">
,
Это означает, что все вышеперечисленное о значении атрибута все еще применяется!
Когда поле ввода находится в режиме файла, оно получает атрибут только для чтения files
который похож на массив FileList object
который ссылается на выбранный пользователем элемент (ы) элемента ввода и является (/ являются) доступным для FileList interface
,
Я упоминал, что files
-атрибут типа FileList
только для чтения (File API, раздел 5.2)?:
Интерфейс HTMLInputElement [HTML] имеет атрибут readonly типа FileList...
Ну, а как насчет перетаскивания?
Из mdn-документации - Выбор файлов с помощью перетаскивания
Настоящее волшебство происходит в функции drop():
function drop(e) { e.stopPropagation(); e.preventDefault(); var dt = e.dataTransfer; var files = dt.files; handleFiles(files); }
Здесь мы извлекаем поле dataTransfer из события, затем извлекаем из него список файлов, передавая его в handleFiles(). С этого момента обработка файлов остается неизменной независимо от того, использовал ли пользователь элемент ввода или перетаскивал его.
Итак, (так же, как поле ввода type="file",) событие dataTransfer
атрибут имеет массивоподобный атрибут files
который похож на массив FileList object
и мы только что узнали (выше), что FileList только для чтения..
FileList содержит ссылки на файл (ы), которые пользователь выбрал (или перетянул на целевой объект), и некоторые атрибуты. Из Файлового API Раздел 7.2 Атрибуты файла мы можем прочитать:
название
Название файла; при получении, это должно вернуть имя файла в виде строки. Существует множество вариантов имен файлов в разных системах; это просто имя файла без информации о пути. При получении, если пользовательские агенты не могут сделать эту информацию доступной, они должны вернуть пустую строку.
lastModifiedDate
Дата последнего изменения файла. При получении, если пользовательские агенты могут сделать эту информацию доступной, она должна вернуть новый объект Date[HTML], инициализированный до даты последнего изменения файла. Если дата и время последнего изменения неизвестны, атрибут должен возвращать текущую дату и время как объект Date.
и есть size
атрибут:
F.size соответствует размеру аргумента blob fileBits, который должен быть неизменными необработанными данными 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
это возвращается из ввода 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
(если только flag_oneTImeOnly не имеет значения true) и может быть отозван с помощьюwindow.URL.revokeObjectURL(myBlobURL)
,
Бинго, вы можете подумать... однако... URL Store
поддерживается только во время сеанса (таким образом, он выдержит обновление страницы, поскольку он все еще остается в том же сеансе) и теряется при выгрузке документа.
Из MDN - Использование URL-адресов объектов:
URL объекта - это строка, идентифицирующая объект File. Каждый раз, когда вы вызываете window.URL.createObjectURL(), создается уникальный URL-адрес объекта, даже если вы уже создали URL-адрес объекта для этого файла. Каждый из них должен быть выпущен. Хотя они высвобождаются автоматически при выгрузке документа, если ваша страница использует их динамически, вы должны явно их высвободить, вызвав window.URL.revokeObjectURL()
Это означает, что даже когда вы храните Blob URL
строка в куки или постоянном локальном хранилище, эта строка будет бесполезна в новом сеансе!
Это должно привести нас к полному кругу и окончательному выводу:
Невозможно (повторно) заполнить поле ввода или выбранный пользователем файл (который находится не в изолированной области браузера "Локальное хранилище").
(Если вы не принуждаете своих пользователей использовать устаревшую версию 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?
Создайте поле ввода в вашей форме. Когда пользователь выбирает файл, скопируйте результат в это поле, например:
jQuery('#inFile').change(
function(){ jQuery('#inCopy').val( jQuery('#inFile').val() ); }
);
На самом деле, результат не копируется точно, вместо этого он копирует "C:/fakepath/SELECTED_FILE_NAME". Хотя вам не разрешено устанавливать значение для ввода файла, вы можете установить значение поля ввода текста без "C:/fakepath/", когда сервер готовит форму.
Теперь, когда сервер вернет форму, проверьте поле ввода текста. Если он начинается с "C:/fakepath/", то пользователь должен выбрать новый файл, поэтому загрузите свой новый выбор. Если это не так, то пользователь выбрал предыдущий выбор, что не должно быть проблемой, поскольку, согласно исходному вопросу, предыдущий выбор был загружен ранее и ДОЛЖЕН (по крайней мере, при соответствующем программировании, он МОЖЕТ) по-прежнему на сервере.
Рискуя наступить на огромную информацию, предоставленную GitaarLAB, я могу предположить, что Дейв Уолли очень близок, предоставляя практическое решение проблемы. Вы оба очень помогли мне, так что спасибо.
Мое домашнее сообщение:
- Ввод файла предлагает улицу с односторонним движением. Он сидит там, ожидая обработки вашей следующей загрузки. Это все, что он делает, в значительной степени. Получает.
- Текстовая форма div или только для чтения, расположенная рядом с меткой для ввода файла, может выполнять эту функцию. то есть его можно использовать, чтобы сказать пользователю: "Вот что загружено в данный момент: " - имя файла для отображения должно быть заполнено из вашей логики на стороне сервера.
Таким образом, основной вариант использования будет:
- пользователь загружает файл через форму ввода на веб-странице
- Серверная логика хранит файл
- В цикле Ajax или перезагрузке веб-страницы серверный код идентифицирует строку fileName для записи в div: "Вот что сейчас загружено"
- Ввод файла также может быть представлен повторно, так что пользователь может загрузить / вместо этого другой файл.
Возможно, вам придется проделать дополнительную работу для обработки "* обязательной" проверки, но это уже другая история.
Если все, что вам действительно нужно, - это временное сохранение данных файла, например, если на странице есть ошибка или если вы хотите иметь страницу подтверждения, перед тем как передать данные в базу данных, то это обычно делается с помощью размещения файлы где-то временные и хранят данные в скрытых полях.
Это не заполняет поле ввода файла. Но вы также можете просто взять имя введенного файла и поместить его рядом с полем ввода файла.
вот так:
<input type=hidden name="filename" value="<?php echo $filename; ?>" />
<input type="file" name="uploadfile" size="50" />
<?php if (!empty($filename)) echo $filename; ?>