Пользовательский FileResult в Azure: браузер ждет вечно

У меня есть действие, которое возвращает Excel как пользовательский FileResult. Мое решение основано на библиотеке ClosedXml (внутренне использующей OpenXml). Мой класс XlsxResult использует файл .xlsx только для чтения на сервере в качестве шаблона. Затем он передает шаблон в поток памяти, который обрабатывается и сохраняется обратно с помощью ClosedXml. В конце поток памяти записывается в ответ.

Это отлично работает как на Cassini, так и на IIS Express, но не работает при развертывании на Azure без каких-либо ошибок. Единственный эффект, который я испытываю, заключается в том, что запрос, отправленный на сервер, никогда не получает ответа. Я все еще жду, что что-то произойдет через 60 минут или около того...

Мое действие:

[OutputCache(Location= System.Web.UI.OutputCacheLocation.None, Duration=0)]
public FileResult Export(int year, int month, int day) {
    var date = new DateTime(year, month, day);
    var filename = string.Format("MyTemplate_{0:yyyyMMdd}.xlsx", date);

    //return new FilePathResult("~/Content/templates/MyTemplate.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

    var result = new XlsxExportTemplatedResult("MyTemplate.xlsx", filename, (workbook) => {
            var ws = workbook.Worksheets.Worksheet("My Export Sheet");
            ws.Cell("B3").Value = date;
            // Using a OpenXML's predefined formats (15 stands for date)
            ws.Cell("B3").Style.NumberFormat.NumberFormatId = 15;
            ws.Columns().AdjustToContents(); // You can also specify the range of columns to adjust, e.g.
            return workbook;
        });
    return result;
}

Мой FileResult

public class XlsxExportTemplatedResult : FileResult
{
    // default buffer size as defined in BufferedStream type
    private const int BufferSize = 0x1000;

    public static readonly string TEMPLATE_FOLDER_LOCATION = @"~\Content\templates";

    public XlsxExportTemplatedResult(string templateName, string fileDownloadName, Func<XLWorkbook, XLWorkbook> generate)
        : base("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") {
        this.TempalteName = templateName;
        this.FileDownloadName = fileDownloadName;
        this.Generate = generate;
    }

    public string TempalteName { get; protected set; }
    public Func<XLWorkbook, XLWorkbook> Generate { get; protected set; }

    protected string templatePath = string.Empty;
    public override void ExecuteResult(ControllerContext context) {
        templatePath = context.HttpContext.Server.MapPath(System.IO.Path.Combine(TEMPLATE_FOLDER_LOCATION, this.TempalteName));
        base.ExecuteResult(context);
    }

    //http://msdn.microsoft.com/en-us/library/office/ee945362(v=office.11).aspx
    protected override void WriteFile(System.Web.HttpResponseBase response) {
        FileStream fileStream = new FileStream(templatePath, FileMode.Open, FileAccess.Read);
        using (MemoryStream memoryStream = new MemoryStream()) {
            CopyStream(fileStream, memoryStream);
            using (var workbook = new XLWorkbook(memoryStream)) {
                Generate(workbook);
                workbook.Save();
            }
            // At this point, the memory stream contains the modified document.
            // grab chunks of data and write to the output stream
            Stream outputStream = response.OutputStream;
            byte[] buffer = new byte[BufferSize];
            while (true) {
                int bytesRead = memoryStream.Read(buffer, 0, BufferSize);
                if (bytesRead == 0) {
                    // no more data
                    break;
                }
                outputStream.Write(buffer, 0, bytesRead);
            }
        }
        fileStream.Dispose();
    }

    static private void CopyStream(Stream source, Stream destination) {
        byte[] buffer = new byte[BufferSize];
        int bytesRead;
        do {
            bytesRead = source.Read(buffer, 0, buffer.Length);
            destination.Write(buffer, 0, bytesRead);
        } while (bytesRead != 0);
    }

}

Так что я что-то пропустил (видимо, я).

Обратите внимание:

  1. В Azure нет отсутствующих DLL, поскольку я проверил с помощью функции RemoteAccess Windows Azure Tools 1.7.
  2. Мой экспорт не является тяжелой длительной задачей.
  3. когда я изменил действие, чтобы просто вернуть FilePathResult с шаблоном xlsx, он работал на Azure. Но мне нужно обработать файл, прежде чем вернуть его, как вы можете подозревать :-)

Танки.

ОБНОВЛЕНИЕ: после того, как я активно вошел в свой код, выполнение зависает без ошибок при вызове метода ClosedXml Save. Но все еще нет ошибки. Аннотация из таблицы WADLogsTable:

  • Открытие файла шаблона по пути: E:\sitesroot\0\Content\templates\MyTemplate.xlsx
  • Открытый шаблон по пути: E:\sitesroot\0\Content\templates\MyTemplate.xlsx только что
  • скопированный шаблон в редактируемый поток памяти. Скопировано байт: 15955, Позиция: 15955
  • изменил документ Excel в памяти.
  • здесь он зависает, когда вызывает workbook.Save(); Это вызов метода ClosedXml.

person cleftheris    schedule 23.08.2012    source источник
comment
Если вы не получаете никаких ошибок, нам будет непросто вам помочь. Попробуйте добавить журналирование между каждой строкой кода, который у вас есть. Таким образом, вы будете точно знать, где он «останавливает» обработку запроса, и мы будем лучше понимать, что происходит.   -  person Sandrino Di Mattia    schedule 23.08.2012
comment
@SandrinoDiMattia Я попытаюсь войти в журнал с трассировкой и вернусь с дополнительной информацией. Но странно то, что в журнале событий нет записей об исключениях, когда я получаю удаленный доступ к одному из экземпляров Azure.   -  person cleftheris    schedule 23.08.2012
comment
@SandrinoDiMattia Я обновил вопрос, добавив дополнительную информацию. Кажется, это проблема с библиотекой ClosedXml, которую я использую. Я скачал исходный код. +1 к вашему комментарию, потому что я чувствую, что куда-то двигаюсь :-)   -  person cleftheris    schedule 04.09.2012
comment
Поэтому я открыл тему на codeplex. Один из координаторов библиотеки ClosedXml предложил попробовать отладить производство с помощью функции Intellitrace, как описано здесь . Я буду публиковать любые обновления.   -  person cleftheris    schedule 04.09.2012
comment
Я не нашел корня к этому. Хотя, изменив свой код на использование EPPlus lib, я избавился от проблемы в Лазурь. Просто публикую альтернативу, так как не нашел ответа на свою проблему.   -  person cleftheris    schedule 02.10.2012


Ответы (1)


Я столкнулся с той же ошибкой, что и вы. Я не могу предложить решение в вашей конкретной ситуации, и я знаю, что вы поменяли треки, но после прохождения тех же разочаровывающих шагов, с которыми вы столкнулись, я хотел бы «проложить путь» для ответа для вас (или других) .

Перейдите в консоль диспетчера пакетов в Visual Studio и установите Elmah с полезными функциями MVC (маршрутизация):

    Install-Package elmah.MVC

Теперь в корневом файле web.config обновите запись Elmah. Вероятно, он находится в конце файла и выглядит так:

    <elmah></elmah>

Обновите этого плохого мальчика, чтобы разрешить удаленный доступ и настроить путь к журналу:

    <elmah>
      <security allowRemoteAccess="1" />
      <errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/app_data/elmah" />
    </elmah>

Теперь передайте это в Azure.

Наконец, посетите свой сайт, вызовите ошибку, а затем перейдите по адресу http://your-site-here.azurewebsites.net/elmah, и вы увидите точную причину ошибки.

Эльма такая классная.

Застенчивое признание: ошибка для меня была не в стороннем коде, а в моей строке подключения, для которой я не установил MultipleActiveResultsSets в значение true. Другое исправление, которое мне пришлось сделать, это передать мои сущности после вызова ToList() одному из внутренних методов этой библиотеки, оставив его, поскольку IQueryable заблокировал метод.

person MisterJames    schedule 26.01.2013
comment
Спасибо за ответ. +1 за предложение фантастической библиотеки elmah! К сожалению, как вы видите в моем вопросе выше, в моем случае я сократил свой код до отсутствия sql, просто чтобы выразить свою точку зрения, прежде чем переключиться на EPPlus. - person cleftheris; 28.01.2013