Последняя версия библиотеки AjaxControlToolkit (сентябрь 2009) порадовала добавлением нового контрола - AsyncFileUpload. Данный контрол (AsyncFileUpload) позволяет загружать файлы на сервер без перегрузки страницы, то есть асинхронно. Так сложилось, что именно в тоже самое время мне пришлось его использовать в одном из проектов. Сообственно, хочу поделиться впечатлениями.
Несмотря на то что AsyncFileUpload выполняет свою основную функцию (асинхронная загрузка файлов на сервер) довольно хорошо, в нём было обнаружено ряд недостатков. Вот на этих недостатках я и сосредоточусь в порядке их обнаружения.
1. Проблемы с фокусом
Когда AsyncFileUpload получает фокус, то последний невожможно переключить с помощью клавишы Tab на клавиатуре. Всё из-за того, что контрол преобразуется в HTML следующего вида:
<input id="some name" type="file" style="opacity: 0; font-size: 14px;" onmousedown="return false;" onkeypress="return false;" onkeydown="return false;" name="ctl00$SampleContent$AsyncFileUpload1$ctl02" size="49"/>
Ключевым моментом здесь является блокировка двух выделенных событий. Чесно говоря я так и не понял до конца зачем это было сделано.
Решение: изначально была препринята попытка удалить аттрибуты с помощью JavaScript, но она не подошла в силу специфики проекта. Решение, которое подошло, не блещет своей красотой и даже наоборот. И так был создан новый класс, который является наследником AsyncFileUpload и в нём перегружена функция CreateChildControls:
protected override void CreateChildControls()
{
base.CreateChildControls();
FieldInfo fi = typeof(AsyncFileUpload).GetField("inputFile", BindingFlags.Instance | BindingFlags.NonPublic);
var control = (HtmlInputFile)fi.GetValue(this);
if (control != null)
{
control.Attributes.Remove("onkeydown");
control.Attributes.Remove("onkeypress");
}
}
Код был написан при помощи беcценного инструмента - Reflector.
2. Query string
Если url странички, на которой лежит AsyncFileUpload, имеет параметры ("/Product/Edit.aspx?id=bike"), то при этом постбэк идёт на url без параметров ("/Product/Edit.aspx"). Данная "фича" принесла немало хлопота, так как при асинхронном запросе страница всёравно проходит свой цикл событий (Init, Load) и в этих событиях параметры недоступны.
Решение: при первой загрузке страницы копировать нужные параметры во ViewState и, сообственно, при асинхронном запросе забить на QueryString и работать с ViewState.
3. Session
На данный момент AsyncFileUpload умеет асинхронно загружать файлы только в сессию пользователя. При этом сама процедура работает кривовато. Файл, загруженный в сессию, после его сохранения на диск из сессии не удаляется.
Решение: после сохранения файла на диск, удалять его из сессии руками.
private void RemoveFileFromSession(string clientId)
{
for (int i = 0; i < Session.Keys.Count; i++)
{
if (Session.Keys[i].Contains(clientId))
{
Session.Remove(Session.Keys[i]);
return;
}
}
}
Похожая функция существует и в самой сборке AjaxControlToolkit в классе AfuPersistedStoreManager, который обьявлен как internal sealed, что ограничивает доступ к нему. Вот её код:
public void RemoveFileFromSession(string controlId, string filename)
{
HttpContext currentContext = null;
currentContext = this.GetCurrentContext();
if (currentContext != null)
{
foreach (string str in currentContext.Session.Keys)
{
if (str.StartsWith(this.GetFullID(controlId)))
{
currentContext.Session.Remove(str);
}
}
}
}
Если верить Рефлектору, а я ему верю, данная функция не используется вообще, хотя некоторые функции из класса AfuPersistedStoreManager используются внутри AsyncFileUpload. Ещё нюанс с этой функцией, что она будет кидать InvalidOperationException, так как коллекция изменяется по ходу её обхода ("Collection was modified; enumeration operation may not execute.").
Ну и напоследок ещё несколько фич, которые мне не понравились или которых не хватает:
1. Загрузка файла начинается сразу же после того как файл был выбран.
2. Нет возможности прервать загрузку или, быть может, я её не увидел.
3. Отсутствует возможность отслеживать прогресс загрузки файла.
Вывод
Подобного рода контрола (AsyncFileUpload) с возможностью асинхронной загрузки файлов не сервер в библиотеке AjaxControlToolkit не хватало, но реализация последнего на данный момент оставляет желать лучшего. Радует только одно, что все недостатки, приведенные выше, можно всё-таки решить. Сообственно, со всеми этими фиксами AsyncFileUpload контрол используется в одном из рабочих проектов.