Завантажте файл Excel через AJAX MVC


92

У мене велика (іш) форма в MVC.

Мені потрібно мати можливість створити файл Excel, що містить дані з підмножини цієї форми.

Хитрощі в тому, що це не повинно впливати на решту форми, і тому я хочу зробити це за допомогою AJAX. Я зіткнувся з кількома запитаннями щодо SO, які, здається, пов’язані, але я не можу повністю зрозуміти, що означають відповіді.

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

Мій файл Excel повертається нормально, якщо я роблю повний пост назад, але я не можу змусити його працювати з AJAX у mvc.

Відповіді:


215

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

ОНОВИТИ вересень 2016 р

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

Поширеним сценарієм у моїх програмах MVC є звітування через веб-сторінку, яка має деякі налаштовані користувачем параметри звітів (діапазони дат, фільтри тощо). Коли користувач вказав параметри, вони публікують їх на сервері, генерується звіт (скажімо, наприклад, файл Excel як вихідний файл), а потім я зберігаю отриманий файл як байтовий масив у TempDataсегменті з унікальним посиланням. Це посилання передається як результат Json на мою функцію AJAX, яка згодом переспрямовує на окрему дію контролера для вилучення даних TempDataта завантаження до браузера кінцевих користувачів.

Щоб надати цю детальнішу інформацію, припускаючи, що у вас є MVC View, який має форму, прив’язану до класу Model, давайте зателефонуємо Model 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 переспрямовує на метод контролера / дії, який фактично повертає ваш файл для завантаження.

Прикладом методу контролера для дії Завантаження буде:

[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
Створення кінцевої точки '/ Завантажити? File = ...' ВИКРИВАЄ значний ризик безпеки - я не фахівець з питань безпеки, але я думаю, що ви хочете додати аутентифікацію користувача, санітарію введення, MVC's [ValidateAntiForgeryToken] та згадати інші найкращі засоби безпеки -практики до цієї відповіді.
Джиммі

2
@CSL Я завжди отримую помилку 0x800a03f6 - Помилка виконання JavaScript: Недійсний символ у відповіді var = JSON.parse (дані);
Стендаж

2
Чудово, чому б вам не поставити стару відповідь внизу? І нова відповідь зверху, щоб люди не витрачали час
goamn

19

Мої 2 центи - вам не потрібно зберігати Excel як фізичний файл на сервері - натомість зберігайте його в кеші (сеансу). Використовуйте унікально сформоване ім'я для вашої змінної Cache (що зберігає файл excel) - це буде повернення вашого (початкового) виклику ajax. Таким чином, вам не доведеться мати справу з проблемами доступу до файлів, керуванням (видаленням) файлів, коли це не потрібно тощо, і, маючи файл у кеші, швидше його отримати.


1
Як саме ви це зробили? Звучить цікаво.
Наталія

2
Приклад був би непоганий (я маю на увазі, як зберігати його в кеш-пам’яті, не генеруючи файл excel).
Тадей

Наскільки це масштабовано? Якщо користувач завантажує кілька великих звітів?
Zapnologica

Якщо ви працюєте в Azure, сеанс буде працювати, ДОКЛИ ви не вимкнете ARRAffinity.
JeeShen Lee

14

Нещодавно мені вдалося досягти цього в 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 = "" });
}

потім створіть дію завантаження

[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

використання 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, успіх: function (Rdata) {налагоджувач; var bytes = new 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 (); },
GVKRAO

хтось Застосував завантаження файлу Excel за посиланням вище, він працює лише для @ html.Beginform (), то після невеликих змін потрібен цей код, для блоку виклику AJAX Success Block, будь ласка, перевірте його, він чудово працює в AJAX CALL
GVKRAO

3
$ .ajax ({
                тип: "ОТРИМАТИ",
                url: "/ Головна / Downloadexcel /",
                contentType: "application / json; charset = utf-8",
                дані: нуль,
                успіх: функція (Rdata) {
                    налагоджувач;
                    var bytes = new 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 ();
                },
                помилка: функція (помилка) {

                }

            });

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 і складається з 2 наступних викликів. Один 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, і я просто хочу завантажити файл із сторони сервера. Є багато статей, але я не можу знайти просто основну відповідь. Тепер я спробував базовий спосіб і отримав його.

Це моя проблема.

Мені доводиться створювати багато кнопок введення динамічно під час виконання. І я хочу додати кожну кнопку для завантаження, додавши унікальний номер файлу.

Я створюю кожну кнопку так:

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;
}

Я створюю цю форму_1, форму_2, форму_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.