MVC3 HttpPostedFileBase Первая загрузка не дает никаких данных, но последующая загрузка
Предыстория вопроса.
РЕДАКТИРОВАТЬ 3 Я могу убедиться, что это снова работает в Chrome 20. Спасибо!
Обновление TLDR
РЕДАКТИРОВАТЬ 2 После еще большего взлома об этом, похоже, ошибка в Chrome 19. Пожалуйста, смотрите здесь:
Ничего похожего на мелкую ошибку!:-)
Firefox-самая последняя и IE8/9 работают как положено. Журналы Fiddler показывают то же самое поведение с тем основным отличием, что 2-я проверка авторизации 401 не посылает искаженную полезную нагрузку формы и работает как ожидалось.
Звучит как тестовый пример, и исправление находится в разработке! Так что, надеюсь, для любого из вас, кто мог столкнуться с этим, это помогает!
КОНЕЦ TLDR
У меня есть действие контроллера, которое принимает только загруженный файл, который представляет собой CSV-файл "событий". За кулисами этот CSV анализируется, превращается в объекты событий, и данные синхронизируются в базе данных. В результате вывода пользователю предлагается загрузить электронную таблицу Excel, которая содержит все ошибки в каждой возникшей строке.
Все это прекрасно работает локально на моей машине для разработки. Однако после развертывания в среде DEV я получаю разные результаты. Первая попытка загрузить файл приводит к тому, что поток, который я ищу здесь, не читается? Он правильно сообщает о своей длине, но попытки извлечь информацию ничего не дает. Последующие чтения имеют данные, и все работает как ожидалось.
Я дам вам соответствующие кодовые страницы. Во-первых, упрощенная форма HTML:
<form action="/Events/ImportLocal" enctype="multipart/form-data" method="post">
<input id="uploadFile" name="uploadFile" type="file" />
<input type="submit" value="Upload Events" />
</form>
Довольно прямо:
Действие контроллера (простите за беспорядок, я взломал это в течение пары часов):
[HttpPost]
public ActionResult ImportLocal(HttpPostedFileBase uploadFile)
{
if (uploadFile == null || uploadFile.ContentLength <= 0)
{
BaseLogger.Info("uploadFile is null or content length is zero...");
//Error content returned to user
}
BaseLogger.InfoFormat("Community Import File Information: Content Length: {0}, Content Type: {1}, File Name: {2}",
uploadFile.ContentLength, uploadFile.ContentType, uploadFile.FileName);
//This logger line is always reporting as correct. Even on the attempts where I can't pull out the stream data, I'm seeing valid values here.
//EXAMPLE output on failed attempts:
//Import File Information: Content Length: 315293, Content Type: application/vnd.ms-excel, File Name: Export_4-30-2012.csv
BaseLogger.InfoFormat("Upload File Input Stream Length: {0}", uploadFile.InputStream.Length);
//This is reporting the correct length on failed attempts as well
//I know the below is overly complicated and convoluted but I'm at a loss for why the inputstream in HttpPostedFileBase is not pulling out as expected
//so I've been testing
var target = new MemoryStream();
uploadFile.InputStream.CopyTo(target);
byte[] data = target.ToArray();
BaseLogger.InfoFormat("File stream resulting length = {0}", data.Length);
//This reports correctly. So far so good.
StringReader stringOut;
var stream = new MemoryStream(data) {Position = 0};
using (var reader = new StreamReader(stream))
{
string output = reader.ReadToEnd();
BaseLogger.InfoFormat("Byte[] converted to string = {0}", output);
//No go...output is reported to be empty at this point so no data ever gets sent on to the service call below
stringOut = new StringReader(output);
}
//Build up a collection of CommunityEvent objects from the CSV file
ImportActionResult<Event> importActionResults = _eventImportServices.Import(stringOut);
Цель состоит в том, чтобы передать текстовый или строковый читатель методу службы, поскольку за кадром это использует реализацию обработки CSV, которая хочет эти типы.
Любые идеи, почему первый запрос не удается, но последующая работа? Я чувствую, что мне нужен свежий взгляд, потому что я иссякаю от идей.
Последний пункт, IIS 7.5 является целью как локально через IIS Express, так и на конечном сервере.
Спасибо
РЕДАКТИРОВАТЬ за вознаграждение:
Я копирую свое сообщение о вознаграждении здесь и вывод Fiddler для каждого запроса
Я добавил несколько комментариев к этому вопросу. Похоже, проблема связана с NTLM. То, что я вижу в Fiddler, - это 401 неавторизованный по запросу 1 с действительными данными формы (точнее, я понимаю, что эти первые два 401 неавторизованных запроса являются "нормальными" в сценариях, где включена только аутентификация Windows; проверьте, пожалуйста?). Перечисленный запрос 2 снова представляет собой 401 с теми же данными формы, за исключением того, что данные загруженного файла теперь представляют собой просто набор блоков данных, точно такого же размера, но не точных загруженных данных. Запрос 3 - это 200 OK, который содержит искаженные данные, и это то, что получает мое действие контроллера. Как заставить NTLM хорошо играть с загрузкой файлов?
Вот вывод Fiddler для каждого запроса:
Запрос 1)
Запрос 2)
И, наконец, запрос 3, который обработан HTTP 200 OK
РЕДАКТИРОВАТЬ 2 После еще большего взлома об этом, похоже, ошибка в Chrome 19. Пожалуйста, смотрите здесь:
Ничего похожего на мелкую ошибку!:-)
Firefox-самая последняя и IE8/9 работают как положено. Журналы Fiddler показывают то же самое поведение с тем основным отличием, что 2-я проверка авторизации 401 не посылает искаженную полезную нагрузку формы и работает как ожидалось.
Звучит как тестовый пример, и исправление находится в разработке! Так что, надеюсь, для любого из вас, кто мог столкнуться с этим, это помогает!
Спасибо
3 ответа
Хороший вопрос. Аутентификация была первым, что пришло мне в голову. Это нормально, чтобы получить два не авторизованных и 3-й авторизованный. Так работает NTLM.
Если нужно, чтобы он также работал на Chrome и не мог дождаться исправления, я бы предложил включить проверку подлинности с помощью форм, которая перенаправляет на тот же домен, но на другой порт, где находится приложение LOGIN с проверкой подлинности Windows. Это приложение устанавливает форму cookie или нет и отправляет вас обратно в основное приложение. Процесс хорошо описан здесь: http://msdn.microsoft.com/en-us/library/ms972958.aspx
Таким образом, ваш NTLM выполняется один раз, а затем у вас есть файл cookie, поэтому NTLM не используется с вашим запросом POST, и все должно работать как положено.
Просто думал, что поделюсь этим:)
Я думаю, что вам нужно проверить права доступа к папке IIS / виртуальному веб-сайту, в который загружаются файлы, и каталогу самого себя, если они находятся в разных местах.
Это просто идея, чтобы попробовать. Обратите внимание, что StreamReader имеет много конструкторов:
http://msdn.microsoft.com/en-us/library/system.io.streamreader.aspx
В некоторых конструкторах он пытается автоматически определить кодировку (если она указана), и если он не может обнаружить ее, он возвращается к указанной. Используемый конструктор использует кодировку UTF-8. Вы можете изменить конструктор так, чтобы он автоматически определял кодировку, а если нет, используйте UTF-8.