Обзор AsyncFileUpload контрола из библиотеки AjaxControlToolkit

1 комментарий

asp-net-ajax.pngПоследняя версия библиотеки 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 контрол используется в одном из рабочих проектов. 

Комментарии

Орлов
 
26.12.2009
Орлов AsyncFileUpload действительно полезная штучка, на практике показывает хорошие результаты

Оставить комментарий