Суть проблемы
Проверить расширение загружаемого файла, если пользователь выбрал только один файл, не составит труда — просто объявляем vtype и смотрим value input-а:
Ext.apply(Ext.form.field.VTypes, { file: function(val, field) { var types = ['rtf', 'pdf', 'doc'], ext = val.substring(val.lastIndexOf('.') + 1); if(Ext.Array.indexOf(types, ext) === -1) { return false; } } ,fileText: 'Invalid file' });
Проблемы начинаются тогда, когда файлов несколько, и/или вам нужно проверить «вес» файла. Все дело в том, что в коде выше field ссылается не на input, в котором «лежат» выбранные файлы, а на table, в которой лежит input.
Достучаться до самого input-а достаточно легко:
var input = Ext.get(field.id+'-fileInputEl'); - и мы можем работать с input.files.
Если кому-то непонятно, то строка выше на vanilla.js будет звучать так:
var input = document.getElementById(field.id+'-fileInputEl');
Наверное ванилла даже сэкономила бы несколько спичек, но я не очень люблю когда начинается каша, а разницы в работе мой организм не ощущает.
Если файл один, и вы не против использования не нативного input-а загрузки файлов, то проблема решена (можно сказать ее и не было) — получаем поле загрузки файла и с помощью File API валидируем вес файла. Иначе — некоторые проблемы остаются:
1. При выборе нескольких файлов (после некоторых манипуляций) поле на форме покажет вам только первый выбранный файл.
2. Если вы хотите нативный input — его атрибут size будет равен 1, после установки inputType: ‘file’ в конфигурации поля, и установка свойства size: 50 вам не поможет.
К вопросу об использовании нативных элементов браузера — нет никакого желания холливарить на эту тему — просто я предпочитаю их использовать, если элемент обладает каким нибудь поведением, и если есть такая возможность. Просто для пользователя это привычнее — он знает чего ожидать от этой штуки.
Хотя, если немного отвлечься от темы, когда ФФ в одном из недавних обновлений перенял Chrome-style поведение атрибута placeholder (текст перестал исчезать при фокусе на поле) — меня это слегка покоробило. Я не понимаю в чем плюсы такого поведения — всегда хочется стереть этот текст перед вводом.
Решение
Если погуглить, интернет, в большинстве случаев, посоветует вам валидировать файлы только на сервере, или использовать flash. Если хочется и можется — можно так и поступить. Еще я видел ux, который умеет грузить много файлов и валидировать их — тоже может быть вариантом.
А можно обойтись буквально несколькими строками, и использовать File API. При этом у нас пока отваливаются всякие IE, но лично я особых переживаний по этому поводу не испытываю — возможность загрузки все равно остается, а на сервере все равно валидировать. К тому же, если вы используете экст и пишите более-менее сложное приложение, то, скорее всего, вы можете диктовать браузеры конечному пользователю.
Поступим следующим образом — унаследуемся от textfield, выставим ему inputType: ‘file’, позволим устанавливать в конфигурации size input-а и атрибут multiple. Назначим всему этому делу alias для «короткого» доступа (xtype). Правила валидации опишем vtype-ом. Все это позволит использовать поле с нужным поведением много раз в любой вьюхе приложения, и гибко его конфигурировать (например, задавать разрешенные типы файлов и их максимальный размер).
Описываем поле:
Ext.define('fileupload',{ extend: 'Ext.form.field.Text' ,alias: 'widget.fileupload' // теперь мы можем написать xtype: 'fileupload' ,inputType: 'file' ,listeners: { render: function (me, eOpts) { var el = Ext.get(me.id+'-inputEl'); el.set({ size: me.inputSize || 1 }); if(me.multiple) { el.set({ multiple: 'multiple' }); } } } });
Тут даже не знаю, нуждается ли что-то в пояснениях. Если в двух словах — после «отрисовки» нативного input-а, для него устанавливается атрибут size (установленный при конфигурации или по дефолту — 1) и атрибут multiple, если он выставлен в конфигурации. Таким образом, теперь у нас есть нативный конфигурируемый input.
Описываем валидатор:
Ext.apply(Ext.form.field.VTypes, { file: function(val, field) { var input, files, file ,acceptSize = field.acceptSize || 4096 // Максимальный вес файлов ,acceptMimes = field.acceptMimes || ['rtf', 'pdf', 'doc', 'xls', 'xlsx', 'zip', 'rar']; // Разрешенные типы input = Ext.get(field.id+'-inputEl'); files = input.getAttribute('files'); if ( ! files || ! window.FileReader) { return true; // Мы имеем дело с неправильным браузером. Из вредности можно вернуть и false } for(var i = 0, l = files.length; i < l; i++) { // смотрим размеры файлов file = files[i]; if(file.size > acceptSize * 1024) { this.fileText = (file.size / 1048576).toFixed(1) + ' MB: invalid file size ('+(acceptSize / 1024).toFixed(1)+' MB max)'; return false; } var ext = file.name.substring(file.name.lastIndexOf('.') + 1); if(Ext.Array.indexOf(acceptMimes, ext) === -1) { // смотрим расширения файлов this.fileText = 'Invalid file type ('+ext+')'; return false; } } return true; } });
Тут, думаю, тоже все достаточно понятно. Просто устанавливаем дефолтные типы/размер файлов, получаем input и пробегаемся по его files — проверяем их тип/размер. Все очень просто.
Теперь мы можем грузить и валидировать файлы, описывая поле формы следующим образом:
{ xtype: 'fileupload' ,vtype: 'file' ,multiple: true // разрешаем выбрать несколько файлов ,acceptMimes: ['doc', 'xls', 'xlsx', 'pdf', 'zip', 'rar'] // Устанавливаем разрешенные типы ,acceptSize: 2048 // Максимальный размер ,inputSize: 76 // Атрибут size ,fieldLabel: 'File <span class="gray">(doc, xls, xlsx, pdf, zip, rar; 2 MB max)</span>' ,msgTarget: 'under' ,name: 'filesToUpload[]' }
В действии все это хозяйство можно посмотреть тут — все собрано в один файл для наглядности, хотя вообще я люблю мвц 4го экста.
P.S.
Долго думал стоит ли мне тут что-то писать, хотя периодически возникает такое желание — хабр таки экспертная тусовка, а я не считаю себя js/extJs джедаем — просто по долгу службы пишу приложения которые работают. К тому же писатель из меня не очень, нету во мне креативности, а экст достоин куда более искусного рассказчика чем я. Да и вообще — экст на хабре мелькает достаточно редко, не знаю, есть ли тут к нему широкий интерес.
Потом дай, думаю, рискну. Авось — кому-то, да пригодится. Картинок нет специально, т.к. я считаю что картинки нужны тогда, когда нужно что-то изобразить, а не когда хочтеся привлечь внимание, а тут изображать ничего не требуется. Ну, если что не так — вы знаете что делать.
ссылка на оригинал статьи http://habrahabr.ru/post/158513/
Добавить комментарий