У меня есть действие, которое возвращает 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);
}
}
Так что я что-то пропустил (видимо, я).
Обратите внимание:
- В Azure нет отсутствующих DLL, поскольку я проверил с помощью функции RemoteAccess Windows Azure Tools 1.7.
- Мой экспорт не является тяжелой длительной задачей.
- когда я изменил действие, чтобы просто вернуть 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.