Загрузите файл Excel через AJAX MVC


93

У меня большая форма в MVC.

Мне нужно создать файл Excel, содержащий данные из подмножества этой формы.

Сложность заключается в том, что это не должно влиять на остальную часть формы, поэтому я хочу сделать это через AJAX. Я столкнулся с несколькими вопросами по SO, которые кажутся связанными, но я не могу понять, что означают ответы.

Это кажется наиболее близким к тому, что мне нужно: asp-net-mvc-download-excel - но я не уверен, что понимаю ответ, и ему уже пару лет. Я также наткнулся на другую статью (больше не могу ее найти) об использовании iframe для обработки загрузки файла, но я не уверен, как заставить это работать с MVC.

Мой файл excel нормально возвращается, если я делаю полный пост, но не могу заставить его работать с AJAX в mvc.

Ответы:


218

Вы не можете напрямую вернуть файл для загрузки через вызов AJAX, поэтому альтернативным подходом является использование вызова AJAX для публикации связанных данных на вашем сервере. Затем вы можете использовать код на стороне сервера для создания файла Excel (я бы рекомендовал использовать для этого EPPlus или NPOI, хотя это звучит так, как будто у вас эта часть работает).

ОБНОВЛЕНИЕ Сентябрь 2016 г.

Мой первоначальный ответ (ниже) был старше 3 лет, поэтому я подумал, что обновлю, поскольку я больше не создаю файлы на сервере при загрузке файлов через AJAX, однако я оставил исходный ответ, поскольку он может быть полезен, все еще в зависимости от ваши конкретные требования.

Распространенный сценарий в моих приложениях MVC - создание отчетов через веб-страницу, на которой есть некоторые параметры отчета, настроенные пользователем (диапазоны дат, фильтры и т. Д.). Когда пользователь указал параметры, которые они отправляют на сервер, создается отчет (например, файл Excel в качестве выходных данных), а затем я сохраняю полученный файл в виде массива байтов в TempDataкорзине с уникальной ссылкой. Эта ссылка возвращается как результат Json в мою функцию AJAX, которая впоследствии перенаправляет на отдельное действие контроллера для извлечения данных TempDataи загрузки в браузер конечных пользователей.

Чтобы представить это более подробно, предположим, что у вас есть представление MVC, которое имеет форму, привязанную к классу модели, позвольте вызвать модель ReportVM.

Во-первых, для получения опубликованной модели требуется действие контроллера, например:

public ActionResult PostReportPartial(ReportVM model){

   // Validate the Model is correct and contains valid data
   // Generate your report output based on the model parameters
   // This can be an Excel, PDF, Word file - whatever you need.

   // As an example lets assume we've generated an EPPlus ExcelPackage

   ExcelPackage workbook = new ExcelPackage();
   // Do something to populate your workbook

   // Generate a new unique identifier against which the file can be stored
   string handle = Guid.NewGuid().ToString();

   using(MemoryStream memoryStream = new MemoryStream()){
        workbook.SaveAs(memoryStream);
        memoryStream.Position = 0;
        TempData[handle] = memoryStream.ToArray();
   }      

   // Note we are returning a filename as well as the handle
   return new JsonResult() { 
         Data = new { FileGuid = handle, FileName = "TestReportOutput.xlsx" }
   };

}

Вызов AJAX, который отправляет мою форму MVC указанному выше контроллеру и получает ответ, выглядит следующим образом:

$ajax({
    cache: false,
    url: '/Report/PostReportPartial',
    data: _form.serialize(), 
    success: function (data){
         var response = JSON.parse(data);
         window.location = '/Report/Download?fileGuid=' + response.FileGuid 
                           + '&filename=' + response.FileName;
    }
})

Действие контроллера для обработки загрузки файла:

[HttpGet]
public virtual ActionResult Download(string fileGuid, string fileName)
{   
   if(TempData[fileGuid] != null){
        byte[] data = TempData[fileGuid] as byte[];
        return File(data, "application/vnd.ms-excel", fileName);
   }   
   else{
        // Problem - Log the error, generate a blank file,
        //           redirect to another controller action - whatever fits with your application
        return new EmptyResult();
   }
}

Еще одно изменение, которое при необходимости может быть легко выполнено, - это передача MIME-типа файла в качестве третьего параметра, чтобы одно действие контроллера могло правильно обслуживать различные форматы выходных файлов.

Это устраняет необходимость в каких-либо физических файлах для создания и хранения на сервере, поэтому никаких дополнительных процедур не требуется, и, опять же, это легко для конечного пользователя.

Обратите внимание: преимущество использования TempDataвместо Sessionзаключается в том, что после TempDataсчитывания данные очищаются, поэтому использование памяти будет более эффективным при большом объеме файловых запросов. См. Рекомендации по использованию TempData .

ОРИГИНАЛЬНЫЙ ответ

Вы не можете напрямую вернуть файл для загрузки через вызов AJAX, поэтому альтернативным подходом является использование вызова AJAX для публикации связанных данных на вашем сервере. Затем вы можете использовать код на стороне сервера для создания файла Excel (я бы рекомендовал использовать для этого EPPlus или NPOI, хотя это звучит так, как будто у вас эта часть работает).

После того, как файл был создан на сервере, верните путь к файлу (или просто имя файла) в качестве возвращаемого значения для вашего вызова AJAX, а затем установите JavaScript window.location для этого URL-адреса, который предложит браузеру загрузить файл.

С точки зрения конечных пользователей, операция загрузки файлов проходит без проблем, поскольку они никогда не покидают страницу, на которой был создан запрос.

Ниже приведен простой надуманный пример вызова ajax для достижения этой цели:

$.ajax({
    type: 'POST',
    url: '/Reports/ExportMyData', 
    data: '{ "dataprop1": "test", "dataprop2" : "test2" }',
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    success: function (returnValue) {
        window.location = '/Reports/Download?file=' + returnValue;
    }
});
  • urlПараметр - это метод контроллера / действия, в котором ваш код создаст файл Excel.
  • Параметр data содержит данные json, которые будут извлечены из формы.
  • returnValue будет именем вашего вновь созданного файла Excel.
  • Команда window.location перенаправляет на метод Controller / Action, который фактически возвращает ваш файл для загрузки.

Примерный метод контроллера для действия Download:

[HttpGet]
public virtual ActionResult Download(string file)
{   
  string fullPath = Path.Combine(Server.MapPath("~/MyFiles"), file);
  return File(fullPath, "application/vnd.ms-excel", file);
}

3
Это выглядит как хороший потенциальный вариант, но прежде чем я его перейду, есть ли другие альтернативы, которые не предполагают предварительного создания файла на сервере?
Валук

4
Не то чтобы я об этом знал - этот подход я успешно использовал много раз. С точки зрения пользователей, это без проблем, единственное, о чем следует помнить, - это то, что вам понадобится рутинная работа по приведению в порядок создаваемых файлов по мере их накопления со временем.
connectedsoftware

7
Создание конечной точки '/ Download? File = ...' КРИЧИТ о большом риске безопасности - я не эксперт по безопасности, но я думаю, что вы захотите добавить аутентификацию пользователя, санацию ввода, MVC [ValidateAntiForgeryToken] и упомянуть другие элементы безопасности. -практики к этому ответу.
Джимми

2
@CSL Я всегда получаю ошибку 0x800a03f6 - Ошибка выполнения JavaScript: недопустимый символ в ответе var = JSON.parse (data);
Standage 06

2
Отлично, почему бы тебе не поместить старый ответ внизу? И новый ответ вверху, чтобы люди не
теряли

19

Мои 2 цента - вам не нужно хранить Excel как физический файл на сервере - вместо этого сохраните его в (сеансовом) кэше. Используйте уникально сгенерированное имя для вашей переменной Cache (в которой хранится этот файл Excel) - это будет результат вашего (начального) вызова ajax. Таким образом, вам не придется решать проблемы с доступом к файлам, управлять (удалять) файлы, когда они не нужны, и т. Д., И, имея файл в кэше, его можно быстрее получить.


1
Как именно вы это сделаете? Звучит интересно.
Наталья

2
Пример был бы хорош (я имею в виду, как сохранить его в кеше, а не генерировать файл excel).
Тадей

Насколько это масштабируемо? Если пользователь скачивает несколько больших отчетов?
Zapnologica

Если вы находитесь в Azure, сеанс будет работать ДО ВЫКЛЮЧЕНИЯ ARRAffinity.
JeeShen Lee

16

Недавно мне удалось сделать это в MVC (хотя не было необходимости использовать AJAX) без создания физического файла, и я решил поделиться своим кодом:

Супер простая функция JavaScript (это вызывает нажатие кнопки datatables.net):

function getWinnersExcel(drawingId) {
    window.location = "/drawing/drawingwinnersexcel?drawingid=" + drawingId;
}

Код контроллера C #:

    public FileResult DrawingWinnersExcel(int drawingId)
    {
        MemoryStream stream = new MemoryStream(); // cleaned up automatically by MVC
        List<DrawingWinner> winnerList = DrawingDataAccess.GetWinners(drawingId); // simple entity framework-based data retrieval
        ExportHelper.GetWinnersAsExcelMemoryStream(stream, winnerList, drawingId);

        string suggestedFilename = string.Format("Drawing_{0}_Winners.xlsx", drawingId);
        return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", suggestedFilename);
    }

В классе ExportHelper я использую сторонний инструмент ( GemBox.Spreadsheet ) для создания файла Excel, и у него есть опция «Сохранить в поток». При этом существует несколько способов создания файлов Excel, которые можно легко записать в поток памяти.

public static class ExportHelper
{
    internal static void GetWinnersAsExcelMemoryStream(MemoryStream stream, List<DrawingWinner> winnerList, int drawingId)
    {

        ExcelFile ef = new ExcelFile();

        // lots of excel worksheet building/formatting code here ...

        ef.SaveXlsx(stream);
        stream.Position = 0; // reset for future read

     }
}

В IE, Chrome и Firefox браузер предлагает загрузить файл, но фактической навигации не происходит.


У меня был похожий подход. Проблема в том, что вы не знаете, когда загрузка закончилась, поэтому можете остановить этот проклятый прелоадер :)
Кэтэлин Рэдой,

8

Сначала создайте действие контроллера, которое создаст файл Excel.

[HttpPost]
public JsonResult ExportExcel()
{
    DataTable dt = DataService.GetData();
    var fileName = "Excel_" + DateTime.Now.ToString("yyyyMMddHHmm") + ".xls";

    //save the file to server temp folder
    string fullPath = Path.Combine(Server.MapPath("~/temp"), fileName);

    using (var exportData = new MemoryStream())
    {
        //I don't show the detail how to create the Excel, this is not the point of this article,
        //I just use the NPOI for Excel handler
        Utility.WriteDataTableToExcel(dt, ".xls", exportData);

        FileStream file = new FileStream(fullPath, FileMode.Create, FileAccess.Write);
        exportData.WriteTo(file);
        file.Close();
    }

    var errorMessage = "you can return the errors in here!";

    //return the Excel file name
    return Json(new { fileName = fileName, errorMessage = "" });
}

затем создайте действие Download

[HttpGet]
[DeleteFileAttribute] //Action Filter, it will auto delete the file after download, 
                      //I will explain it later
public ActionResult Download(string file)
{
    //get the temp folder and file path in server
    string fullPath = Path.Combine(Server.MapPath("~/temp"), file);

    //return the file for download, this is an Excel 
    //so I set the file content type to "application/vnd.ms-excel"
    return File(fullPath, "application/vnd.ms-excel", file);
}

если вы хотите удалить файл после загрузки, создайте это

public class DeleteFileAttribute : ActionFilterAttribute
{
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        filterContext.HttpContext.Response.Flush();

        //convert the current filter context to file and get the file path
        string filePath = (filterContext.Result as FilePathResult).FileName;

        //delete the file after download
        System.IO.File.Delete(filePath);
    }
}

и, наконец, вызов ajax из вашего представления MVC Razor

//I use blockUI for loading...
$.blockUI({ message: '<h3>Please wait a moment...</h3>' });    
$.ajax({
    type: "POST",
    url: '@Url.Action("ExportExcel","YourController")', //call your controller and action
    contentType: "application/json; charset=utf-8",
    dataType: "json",
}).done(function (data) {
    //console.log(data.result);
    $.unblockUI();

    //get the file name for download
    if (data.fileName != "") {
        //use window.location.href for redirect to download action for download the file
        window.location.href = "@Url.RouteUrl(new 
            { Controller = "YourController", Action = "Download"})/?file=" + data.fileName;
    }
});

7

Я использовал решение, опубликованное CSL, но я бы рекомендовал вам не сохранять данные файла в сеансе в течение всего сеанса. При использовании TempData данные файла автоматически удаляются после следующего запроса (который является запросом GET для файла). Вы также можете управлять удалением данных файла в сеансе в действии загрузки.

Сеанс может потреблять много памяти / места в зависимости от хранилища SessionState и количества файлов, экспортируемых во время сеанса, и если у вас много пользователей.

Я обновил код стороны serer из CSL, чтобы вместо этого использовать TempData.

public ActionResult PostReportPartial(ReportVM model){

   // Validate the Model is correct and contains valid data
   // Generate your report output based on the model parameters
   // This can be an Excel, PDF, Word file - whatever you need.

   // As an example lets assume we've generated an EPPlus ExcelPackage

   ExcelPackage workbook = new ExcelPackage();
   // Do something to populate your workbook

   // Generate a new unique identifier against which the file can be stored
   string handle = Guid.NewGuid().ToString()

   using(MemoryStream memoryStream = new MemoryStream()){
        workbook.SaveAs(memoryStream);
        memoryStream.Position = 0;
        TempData[handle] = memoryStream.ToArray();
   }      

   // Note we are returning a filename as well as the handle
   return new JsonResult() { 
         Data = new { FileGuid = handle, FileName = "TestReportOutput.xlsx" }
   };

}

[HttpGet]
public virtual ActionResult Download(string fileGuid, string fileName)
{   
   if(TempData[fileGuid] != null){
        byte[] data = TempData[fileGuid] as byte[];
        return File(data, "application/vnd.ms-excel", fileName);
   }   
   else{
        // Problem - Log the error, generate a blank file,
        //           redirect to another controller action - whatever fits with your application
        return new EmptyResult();
   }
}

@Nichlas Я тоже начал использовать TempData, ваш ответ побудил меня обновить мой, чтобы отразить это!
connectedsoftware

5

using ClosedXML.Excel;

   public ActionResult Downloadexcel()
    {   
        var Emplist = JsonConvert.SerializeObject(dbcontext.Employees.ToList());
        DataTable dt11 = (DataTable)JsonConvert.DeserializeObject(Emplist, (typeof(DataTable)));
        dt11.TableName = "Emptbl";
        FileContentResult robj;
        using (XLWorkbook wb = new XLWorkbook())
        {
            wb.Worksheets.Add(dt11);
            using (MemoryStream stream = new MemoryStream())
            {
                wb.SaveAs(stream);
                var bytesdata = File(stream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "myFileName.xlsx");
                robj = bytesdata;
            }
        }


        return Json(robj, JsonRequestBehavior.AllowGet);
    }


В блоке успеха AJAX CALL Success: function (Rdata) {debugger; var bytes = новый Uint8Array (Rdata.FileContents); var blob = new Blob ([байты], {type: "application / vnd.openxmlformats-officedocument.spreadsheetml.sheet"}); var link = document.createElement ('a'); link.href = window.URL.createObjectURL (blob); link.download = "myFileName.xlsx"; link.click (); },
ГВКРАО 06

Кто-то реализовал загрузку файла Excel по приведенной выше ссылке, он работает только для @ html.Beginform (), затем после небольших изменений этот код требуется для блока успеха вызова AJAX, проверьте его, он отлично работает в AJAX CALL
GVKRAO

3
$ .ajax ({
                тип: "ПОЛУЧИТЬ",
                url: "/ Home / Downloadexcel /",
                contentType: "application / json; charset = utf-8",
                данные: нуль,
                успех: функция (Rdata) {
                    отладчик;
                    var bytes = новый Uint8Array (Rdata.FileContents); 
                    var blob = new Blob ([байты], {type: "application / vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
                    var link = document.createElement ('a');
                    link.href = window.URL.createObjectURL (blob);
                    link.download = "myFileName.xlsx";
                    link.click ();
                },
                error: function (err) {

                }

            });

1

Принятый ответ не совсем сработал для меня, так как я получил результат 502 Bad Gateway от вызова ajax, хотя все, казалось, возвращалось с контроллера нормально.

Возможно, я достиг предела с TempData - не уверен, но я обнаружил, что если я использовал IMemoryCache вместо TempData , он работал нормально, поэтому вот моя адаптированная версия кода в принятом ответе:

public ActionResult PostReportPartial(ReportVM model){

   // Validate the Model is correct and contains valid data
   // Generate your report output based on the model parameters
   // This can be an Excel, PDF, Word file - whatever you need.

   // As an example lets assume we've generated an EPPlus ExcelPackage

   ExcelPackage workbook = new ExcelPackage();
   // Do something to populate your workbook

   // Generate a new unique identifier against which the file can be stored
   string handle = Guid.NewGuid().ToString();

   using(MemoryStream memoryStream = new MemoryStream()){
        workbook.SaveAs(memoryStream);
        memoryStream.Position = 0;
        //TempData[handle] = memoryStream.ToArray();

        //This is an equivalent to tempdata, but requires manual cleanup
        _cache.Set(handle, memoryStream.ToArray(), 
                    new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(10))); 
                    //(I'd recommend you revise the expiration specifics to suit your application)

   }      

   // Note we are returning a filename as well as the handle
   return new JsonResult() { 
         Data = new { FileGuid = handle, FileName = "TestReportOutput.xlsx" }
   };

}

Вызов AJAX остается таким же, как и в принятом ответе (я не вносил изменений):

$ajax({
    cache: false,
    url: '/Report/PostReportPartial',
    data: _form.serialize(), 
    success: function (data){
         var response = JSON.parse(data);
         window.location = '/Report/Download?fileGuid=' + response.FileGuid 
                           + '&filename=' + response.FileName;
    }
})

Действие контроллера для обработки загрузки файла:

[HttpGet]
public virtual ActionResult Download(string fileGuid, string fileName)
{   
    if (_cache.Get<byte[]>(fileGuid) != null)
    {
        byte[] data = _cache.Get<byte[]>(fileGuid);
        _cache.Remove(fileGuid); //cleanup here as we don't need it in cache anymore
        return File(data, "application/vnd.ms-excel", fileName);
    }
    else
    {
        // Something has gone wrong...
        return View("Error"); // or whatever/wherever you want to return the user
    }
}

...

Теперь есть дополнительный код для настройки MemoryCache ...

Чтобы использовать "_cache", я ввел в конструктор контроллера вот так:

using Microsoft.Extensions.Caching.Memory;
namespace MySolution.Project.Controllers
{
 public class MyController : Controller
 {
     private readonly IMemoryCache _cache;

     public LogController(IMemoryCache cache)
     {
        _cache = cache;
     }

     //rest of controller code here
  }
 }

И убедитесь, что у вас есть следующее в ConfigureServices в Startup.cs:

services.AddDistributedMemoryCache();

0

Эта ветка помогла мне создать собственное решение, которым я поделюсь здесь. Сначала я без проблем использовал запрос GET ajax, но дошел до того, что длина URL-адреса запроса была превышена, поэтому мне пришлось переключиться на POST.

В javascript используется плагин загрузки файлов JQuery, который состоит из двух последовательных вызовов. Один POST (для отправки параметров) и один GET для получения файла.

 function download(result) {
        $.fileDownload(uri + "?guid=" + result,
        {
            successCallback: onSuccess.bind(this),
            failCallback: onFail.bind(this)
        });
    }

    var uri = BASE_EXPORT_METADATA_URL;
    var data = createExportationData.call(this);

    $.ajax({
        url: uri,
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify(data),
        success: download.bind(this),
        fail: onFail.bind(this)
    });

Сторона сервера

    [HttpPost]
    public string MassExportDocuments(MassExportDocumentsInput input)
    {
        // Save query for file download use
        var guid = Guid.NewGuid();
        HttpContext.Current.Cache.Insert(guid.ToString(), input, null, DateTime.Now.AddMinutes(5), Cache.NoSlidingExpiration);
        return guid.ToString();
    }

   [HttpGet]
    public async Task<HttpResponseMessage> MassExportDocuments([FromUri] Guid guid)
    {
        //Get params from cache, generate and return
        var model = (MassExportDocumentsInput)HttpContext.Current.Cache[guid.ToString()];
          ..... // Document generation

        // to determine when file is downloaded
        HttpContext.Current
                   .Response
                   .SetCookie(new HttpCookie("fileDownload", "true") { Path = "/" });

        return FileResult(memoryStream, "documents.zip", "application/zip");
    }

0

Ответ CSL был реализован в проекте, над которым я работаю, но проблема, с которой я столкнулся, заключалась в том, что масштабирование в Azure нарушило наши загрузки файлов. Вместо этого я смог сделать это с помощью одного вызова AJAX:

СЕРВЕР

[HttpPost]
public FileResult DownloadInvoice(int id1, int id2)
{
    //necessary to get the filename in the success of the ajax callback
    HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");

    byte[] fileBytes = _service.GetInvoice(id1, id2);
    string fileName = "Invoice.xlsx";
    return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}

КЛИЕНТ (измененная версия загрузки файла Handle из сообщения ajax )

$("#downloadInvoice").on("click", function() {
    $("#loaderInvoice").removeClass("d-none");

    var xhr = new XMLHttpRequest();
    var params = [];
    xhr.open('POST', "@Html.Raw(Url.Action("DownloadInvoice", "Controller", new { id1 = Model.Id1, id2 = Model.Id2 }))", true);
    xhr.responseType = 'arraybuffer';
    xhr.onload = function () {
        if (this.status === 200) {
            var filename = "";
            var disposition = xhr.getResponseHeader('Content-Disposition');
            if (disposition && disposition.indexOf('attachment') !== -1) {
                var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                var matches = filenameRegex.exec(disposition);
                if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
            }
            var type = xhr.getResponseHeader('Content-Type');

            var blob = typeof File === 'function'
                ? new File([this.response], filename, { type: type })
                : new Blob([this.response], { type: type });
            if (typeof window.navigator.msSaveBlob !== 'undefined') {
                // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
                window.navigator.msSaveBlob(blob, filename);
            } else {
                var URL = window.URL || window.webkitURL;
                var downloadUrl = URL.createObjectURL(blob);

                if (filename) {
                    // use HTML5 a[download] attribute to specify filename
                    var a = document.createElement("a");
                    // safari doesn't support this yet
                    if (typeof a.download === 'undefined') {
                        window.location = downloadUrl;
                    } else {
                        a.href = downloadUrl;
                        a.download = filename;
                        document.body.appendChild(a);
                        a.click();
                    }
                } else {
                    window.location = downloadUrl;

                }

                setTimeout(function() {
                        URL.revokeObjectURL(downloadUrl);
                    $("#loaderInvoice").addClass("d-none");
                }, 100); // cleanup
            }
        }
    };
    xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    xhr.send($.param(params));
});

0
  $.ajax({
    global: false,
    url: SitePath + "/User/ExportTeamMembersInExcel",
    "data": { 'UserName': UserName, 'RoleId': RoleId, UserIds: AppraseeId },
    "type": "POST",
    "dataType": "JSON",
   "success": function (result) {
        debugger
        var bytes = new Uint8Array(result.FileContents);
        var blob = new Blob([bytes], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = "myFileName.xlsx";
        link.click();
      },
    "error": function () {
        alert("error");
    }
})


[HttpPost]
    public JsonResult ExportTeamMembersInExcel(string UserName, long? RoleId, string[] UserIds)
    {
        MemoryStream stream = new MemoryStream();
        FileContentResult robj;
        DataTable data = objuserservice.ExportTeamToExcel(UserName, RoleId, UserIds);
        using (XLWorkbook wb = new XLWorkbook())
        {
            wb.Worksheets.Add(data, "TeamMembers");
            using (stream)
            {
                wb.SaveAs(stream);
            }
        }
        robj = File(stream.ToArray(), System.Net.Mime.MediaTypeNames.Application.Octet, "TeamMembers.xlsx");
        return Json(robj, JsonRequestBehavior.AllowGet);
    }

не может открыть файл, excel просто открывается, а затем не закрывается, я даже добавил stream.close () непосредственно перед robj, но не работал.
dawncode

0

Я могу показаться довольно наивным и может вызвать серьезную критику, но вот как я это сделал
( это не ajaxкасается экспорта, но и не выполняет полную обратную передачу )

Спасибо за этот пост и за этот ответ.
Создайте простой контроллер

public class HomeController : Controller
{               
   /* A demo action
    public ActionResult Index()
    {           
        return View(model);
    }
   */
    [HttpPost]
    public FileResult ExportData()
    {
        /* An example filter
        var filter = TempData["filterKeys"] as MyFilter;
        TempData.Keep();            */
        var someList = db.GetDataFromDb(/*filter*/) // filter as an example

    /*May be here's the trick, I'm setting my filter in TempData["filterKeys"] 
     in an action,(GetFilteredPartial() illustrated below) when 'searching' for the data,
     so do not really need ajax here..to pass my filters.. */

     //Some utility to convert list to Datatable
     var dt = Utility.ConvertToDataTable(someList); 

      //  I am using EPPlus nuget package 
      using (ExcelPackage pck = new ExcelPackage())
      {
          ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Sheet1");
          ws.Cells["A1"].LoadFromDataTable(dt, true);

            using (var memoryStream = new MemoryStream())
            {                   
              pck.SaveAs(memoryStream);
              return File(memoryStream.ToArray(),
              "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
              "ExportFileName.xlsx");                    
            }                
        }   
    }

    //This is just a supporting example to illustrate setting up filters ..        
   /* [HttpPost]
    public PartialViewResult GetFilteredPartial(MyFilter filter)
    {            
        TempData["filterKeys"] = filter;
        var filteredData = db.GetConcernedData(filter);
        var model = new MainViewModel();
        model.PartialViewModel = filteredData;

        return PartialView("_SomePartialView", model);
    } */     
} 

А вот и просмотры ..

/*Commenting out the View code, in order to focus on the imp. code     
 @model Models.MainViewModel
 @{Layout...}     

      Some code for, say, a partial View  
      <div id="tblSampleBody">
        @Html.Partial("_SomePartialView", Model.PartialViewModel)
      </div>
  */                                                       
//The actual part.. Just **posting** this bit of data from the complete View...
//Here, you are not posting the full Form..or the complete View
   @using (Html.BeginForm("ExportData", "Home", FormMethod.Post))
    {
        <input type="submit" value="Export Data" />
    }
//...
//</div>

/*And you may require to pass search/filter values.. as said in the accepted answer..
That can be done while 'searching' the data.. and not while
 we need an export..for instance:-             

<script>             
  var filterData = {
      SkipCount: someValue,
      TakeCount: 20,
      UserName: $("#UserName").val(),
      DepartmentId: $("#DepartmentId").val(),     
   }

  function GetFilteredData() {
       $("#loader").show();
       filterData.SkipCount = 0;
       $.ajax({
          url: '@Url.Action("GetFilteredPartial","Home")',
          type: 'POST',
          dataType: "html",
          data: filterData,
          success: function (dataHTML) {
          if ((dataHTML === null) || (dataHTML == "")) {
              $("#tblSampleBody").html('<tr><td>No Data Returned</td></tr>');
                $("#loader").hide();
            } else {
                $("#tblSampleBody").html(dataHTML);                    
                $("#loader").hide();
            }
        }
     });
   }    
</script>*/

Вся суть уловки кажется в том, что мы публикуем форму ( часть Razor View), для которой мы вызываем an Action method, который возвращает: a FileResult, и это FileResultвозвращает the Excel File..
А для публикации значений фильтра, как сказано, ( и если вам это нужно), я отправляю запрос на публикацию другого действия, как это пытались описать ..


-1

Я использую Asp.Net WebForm и просто хочу загрузить файл со стороны сервера. Есть много статей, но я не могу найти простой ответ. Я попробовал простой способ и получил его.

Это моя проблема.

Мне нужно создать много кнопок ввода динамически во время выполнения. И я хочу добавить каждую кнопку для кнопки загрузки с указанием уникального fileNumber.

Каждую кнопку я создаю так:

fragment += "<div><input type=\"button\" value=\"Create Excel\" onclick=\"CreateExcelFile(" + fileNumber + ");\" /></div>";

Каждая кнопка вызывает этот метод ajax.

$.ajax({
    type: 'POST',
    url: 'index.aspx/CreateExcelFile',
    data: jsonData,
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    success: function (returnValue) {
      window.location = '/Reports/Downloads/' + returnValue.d;
    }
});

Затем я написал базовый простой метод.

[WebMethod]
public static string CreateExcelFile2(string fileNumber)
{
    string filePath = string.Format(@"Form_{0}.xlsx", fileNumber);
    return filePath;
}

Я создаю эти Form_1, Form_2, Form_3 .... И я собираюсь удалить эти старые файлы с помощью другой программы. Но если есть способ просто отправить массив байтов для загрузки файла, например, с помощью Response. Я хочу его использовать.

Надеюсь, это будет полезно для всех.


-1

В форме отправки

public ActionResult ExportXls()
{   
 var filePath="";
  CommonHelper.WriteXls(filePath, "Text.xls");
}

 public static void WriteXls(string filePath, string targetFileName)
    {
        if (!String.IsNullOrEmpty(filePath))
        {
            HttpResponse response = HttpContext.Current.Response;
            response.Clear();
            response.Charset = "utf-8";
            response.ContentType = "text/xls";
            response.AddHeader("content-disposition", string.Format("attachment; filename={0}", targetFileName));
            response.BinaryWrite(File.ReadAllBytes(filePath));
            response.End();
        }
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.