Baixe o arquivo do Excel via AJAX MVC


92

Eu tenho um formulário grande (ish) em MVC.

Preciso ser capaz de gerar um arquivo Excel contendo dados de um subconjunto desse formulário.

A parte complicada é que isso não deve afetar o resto do formulário, por isso quero fazer isso via AJAX. Encontrei algumas perguntas sobre SO que parecem estar relacionadas, mas não consigo entender o que as respostas significam.

Este parece ser o mais próximo do que estou procurando: asp-net-mvc-download-excel - mas não tenho certeza se entendi a resposta, e já tem alguns anos. Também me deparei com outro artigo (não consigo mais encontrar) sobre o uso de um iframe para lidar com o download do arquivo, mas não tenho certeza de como fazer isso funcionar com MVC.

Meu arquivo excel retorna bem se eu estou fazendo uma postagem completa de volta, mas não consigo fazê-lo funcionar com AJAX no mvc.

Respostas:


215

Você não pode retornar um arquivo diretamente para download por meio de uma chamada AJAX, portanto, uma abordagem alternativa é usar uma chamada AJAX para postar os dados relacionados em seu servidor. Em seguida, você pode usar o código do lado do servidor para criar o arquivo do Excel (eu recomendaria usar EPPlus ou NPOI para isso, embora pareça que você tem essa parte funcionando).

ATUALIZAÇÃO setembro 2016

Minha resposta original (abaixo) tinha mais de 3 anos, então pensei em atualizar, pois não crio mais arquivos no servidor ao baixar arquivos via AJAX, no entanto, deixei a resposta original, pois pode ser útil ainda dependendo de seus requisitos específicos.

Um cenário comum em meus aplicativos MVC é o relatório por meio de uma página da web que tem alguns parâmetros de relatório configurados pelo usuário (intervalos de datas, filtros etc.). Quando o usuário especifica os parâmetros que eles publicam no servidor, o relatório é gerado (digamos, por exemplo, um arquivo Excel como saída) e, em seguida, armazeno o arquivo resultante como uma matriz de bytes no TempDataintervalo com uma referência exclusiva. Essa referência é passada de volta como um Resultado Json para minha função AJAX que, subsequentemente, redireciona para a ação do controlador separado para extrair os dados TempDatae baixar para o navegador do usuário final.

Para dar mais detalhes, supondo que você tenha uma MVC View que tem um formulário vinculado a uma classe Model, vamos chamar o Model ReportVM.

Primeiro, uma ação do controlador é necessária para receber o modelo postado, um exemplo seria:

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

}

A chamada AJAX que envia meu formulário MVC para o controlador acima e recebe a resposta é semelhante a esta:

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

A ação do controlador para lidar com o download do arquivo:

[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();
   }
}

Uma outra mudança que poderia ser facilmente acomodada, se necessária, é passar o tipo MIME do arquivo como um terceiro parâmetro para que a única ação do controlador possa servir corretamente a uma variedade de formatos de arquivo de saída.

Isso elimina qualquer necessidade de qualquer arquivo físico criado e armazenado no servidor, de forma que nenhuma rotina de manutenção seja necessária e, mais uma vez, isso é perfeito para o usuário final.

Observe que a vantagem de usar em TempDatavez de Sessioné que, uma vez TempDatalidos, os dados são apagados, portanto, será mais eficiente em termos de uso de memória se você tiver um alto volume de solicitações de arquivo. Consulte a prática recomendada de TempData .

Resposta ORIGINAL

Você não pode retornar um arquivo diretamente para download por meio de uma chamada AJAX, portanto, uma abordagem alternativa é usar uma chamada AJAX para postar os dados relacionados em seu servidor. Você pode então usar o código do lado do servidor para criar o arquivo do Excel (eu recomendaria usar EPPlus ou NPOI para isso, embora pareça que você tem essa parte funcionando).

Depois que o arquivo tiver sido criado no servidor, devolva o caminho para o arquivo (ou apenas o nome do arquivo) como o valor de retorno para sua chamada AJAX e, em seguida, defina o JavaScript window.locationpara este URL que solicitará ao navegador para baixar o arquivo.

Do ponto de vista dos usuários finais, a operação de download do arquivo é contínua, pois eles nunca saem da página de origem da solicitação.

Abaixo está um exemplo planejado simples de uma chamada de ajax para conseguir isso:

$.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;
    }
});
  • O parâmetro url é o método Controlador / Ação onde seu código criará o arquivo Excel.
  • O parâmetro data contém os dados json que seriam extraídos do formulário.
  • returnValue seria o nome do arquivo do seu arquivo Excel recém-criado.
  • O comando window.location redireciona para o método Controller / Action que realmente retorna seu arquivo para download.

Um método de controlador de amostra para a ação de download seria:

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

3
Essa parece uma boa opção potencial, mas antes de prosseguir, não há outras alternativas que não envolvam a criação do arquivo primeiro no servidor?
Valuk de

4
Não que eu saiba - usei essa abordagem com sucesso muitas vezes. Do ponto de vista do usuário, é perfeito, a única coisa a se ter em mente é que você precisará de uma rotina de limpeza para organizar os arquivos que são criados à medida que vão sendo montados com o tempo.
software conectado de

7
Criando um endpoint '/ Download? File = ...' GRITA um grande risco de segurança - Não sou nenhum especialista em segurança, mas acho que você gostaria de adicionar autenticação de usuário, saneamento de entrada, MVC's [ValidateAntiForgeryToken] e mencionar outros tipos de segurança melhor -práticas para esta resposta.
Jimmy

2
@CSL Sempre recebo o erro 0x800a03f6 - Erro de tempo de execução do JavaScript: caractere inválido na resposta var = JSON.parse (dados);
Standage

2
Ótimo, por que você não coloca a resposta antiga no final? E a nova resposta no topo, para que as pessoas não
percam

19

Meus 2 centavos - você não precisa armazenar o Excel como um arquivo físico no servidor - em vez disso, armazene-o no Cache (Sessão). Use um nome gerado exclusivamente para sua variável de Cache (que armazena aquele arquivo do Excel) - este será o retorno de sua chamada ajax (inicial). Desta forma, você não precisa lidar com problemas de acesso ao arquivo, gerenciando (deletando) os arquivos quando não são necessários, etc. e, tendo o arquivo no Cache, é mais rápido recuperá-lo.


1
Como exatamente você faria isso? Soa interessante.
Natalia

2
Um exemplo seria bom (quero dizer como armazená-lo no cache, não gerando o arquivo excel).
Tadej

Quão escalável é isso? Se um usuário estiver baixando vários relatórios grandes?
Zapnologica de

Se você estiver no Azure, a sessão funcionará ATÉ que você DESLIGUE o ARRAffinity.
JeeShen Lee

14

Recentemente, consegui fazer isso em MVC (embora não houvesse necessidade de usar AJAX) sem criar um arquivo físico e pensei em compartilhar meu código:

Função JavaScript super simples (o clique do botão datatables.net aciona isso):

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

Código do controlador 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);
    }

Na classe ExportHelper eu uso uma ferramenta de terceiros ( GemBox.Spreadsheet ) para gerar o arquivo Excel e tem uma opção Salvar no Fluxo. Dito isso, há várias maneiras de criar arquivos Excel que podem ser facilmente gravados em um fluxo de memória.

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

     }
}

No IE, Chrome e Firefox, o navegador solicita o download do arquivo e nenhuma navegação real ocorre.


8

Primeiro crie a ação do controlador que criará o arquivo 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 = "" });
}

em seguida, crie a ação 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);
}

se você deseja excluir o arquivo após o download, crie este

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

e, finalmente, ligue para ajax de sua visualização 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

Usei a solução postada por CSL, mas recomendo que você não armazene os dados do arquivo na sessão durante toda a sessão. Usando TempData, os dados do arquivo são removidos automaticamente após a próxima solicitação (que é a solicitação GET para o arquivo). Você também pode gerenciar a remoção dos dados do arquivo na sessão em ação de download.

A sessão pode consumir muita memória / espaço dependendo do armazenamento do SessionState e de quantos arquivos são exportados durante a sessão e se você tem muitos usuários.

Eu atualizei o código do lado do seridor de CSL para usar 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 eu comecei a usar TempData também, sua resposta me levou a atualizar a minha para refletir isso!
conectado

5

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


Em AJAX CALL Success Block, success: function (Rdata) {debugger; var bytes = novo Uint8Array (Rdata.FileContents); var blob = new Blob ([bytes], {type: "application / vnd.openxmlformats-officedocument.spreadsheetml.sheet"}); var link = document.createElement ('a'); link.href = janela.URL.createObjectURL (blob); link.download = "myFileName.xlsx"; link.click (); },
GVKRAO

alguém tem implementado Excel download do arquivo no link acima, ele funciona apenas para @ Html.BeginForm () depois de pequenas mudanças precisam que o código, para a chamada AJAX Sucesso Block, por favor verificá-lo, ele funciona muito bem em AJAX CHAMADA
GVKRAO

3
$ .ajax ({
                digite: "GET",
                url: "/ Home / Downloadexcel /",
                contentType: "application / json; charset = utf-8",
                dados: nulo,
                sucesso: função (Rdata) {
                    depurador;
                    var bytes = novo Uint8Array (Rdata.FileContents); 
                    var blob = new Blob ([bytes], {type: "application / vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
                    var link = document.createElement ('a');
                    link.href = janela.URL.createObjectURL (blob);
                    link.download = "myFileName.xlsx";
                    link.click ();
                },
                erro: função (err) {

                }

            });

1

A resposta aceita não funcionou muito bem para mim, pois obtive um resultado de 502 Bad Gateway da chamada ajax, embora tudo parecesse estar retornando bem do controlador.

Talvez eu estivesse atingindo um limite com TempData - não tenho certeza, mas descobri que se usei IMemoryCache em vez de TempData , funcionou bem, então aqui está minha versão adaptada do código na resposta aceita:

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

}

A chamada AJAX permanece como com a resposta aceita (não fiz alterações):

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

A ação do controlador para lidar com o download do arquivo:

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

...

Agora, há algum código extra para configurar o MemoryCache ...

Para usar "_cache" injetei no construtor do controlador assim:

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

E certifique-se de ter o seguinte em ConfigureServices em Startup.cs:

services.AddDistributedMemoryCache();

0

Este tópico me ajudou a criar minha própria solução que compartilharei aqui. Eu estava usando uma solicitação GET ajax a princípio sem problemas, mas cheguei a um ponto em que o comprimento da URL da solicitação foi excedido, então tive que mudar para um POST.

O javascript usa o plugin de download de arquivo JQuery e consiste em 2 chamadas sucessivas. Um POST (para enviar parâmetros) e um GET para recuperar o arquivo.

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

Lado do servidor

    [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

A resposta de CSL foi implementada em um projeto no qual estou trabalhando, mas o problema em que incorri foi o dimensionamento no Azure quebrou nossos downloads de arquivos. Em vez disso, consegui fazer isso com uma chamada AJAX:

SERVIDOR

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

CLIENT (versão modificada do download do arquivo Handle da postagem 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);
    }

não consigo abrir o arquivo, o excel apenas abre e não fecha sozinho, eu até adicionei stream.close () antes de robj, mas não está funcionando.
código da madrugada

0

I pode soar bastante ingênuo, e pode atrair bastante crítica, mas aqui está como eu fiz isso,
( Ela não envolve ajaxpara exportação, mas não faz um postback completo, quer )

Obrigado por este post e esta resposta.
Crie um controlador simples

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);
    } */     
} 

E aqui estão as visualizações ..

/*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>*/

Todo o ponto do truque parece que estamos postando um formulário (uma parte da Visão do Razor) no qual estamos chamando um Action method, que retorna: a FileResult, e este FileResultretorna the Excel File..
E para postar os valores do filtro, como disse, ( e se você precisar), estou fazendo uma solicitação de postagem para outra ação, conforme tentamos descrever ..


-1

Estou usando o Asp.Net WebForm e só quero baixar um arquivo do lado do servidor. Há muito artigo, mas não consigo encontrar apenas uma resposta básica. Agora, tentei uma maneira básica e consegui.

Esse é meu problema.

Eu tenho que criar muitos botões de entrada dinamicamente no tempo de execução. E eu quero adicionar cada botão para botão de download dando um único fileNumber.

Eu crio cada botão assim:

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

Cada botão chama esse método 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;
    }
});

Então escrevi um método básico simples.

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

Estou gerando este Form_1, Form_2, Form_3 .... E vou deletar esses arquivos antigos com outro programa. Mas se houver uma maneira de apenas enviar matriz de bytes para baixar o arquivo, como usar o Response. Eu quero usar isso.

Espero que seja útil para alguém.


-1

Ao enviar formulário

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();
        }
    }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.