AJAX MVC를 통해 Excel 파일 다운로드


92

MVC에 큰 (ish) 형태가 있습니다.

해당 양식의 하위 집합에서 데이터를 포함하는 Excel 파일을 생성 할 수 있어야합니다.

까다로운 부분은 이것이 양식의 나머지 부분에 영향을주지 않아야한다는 것이므로 AJAX를 통해 수행하고 싶습니다. 나는 관련된 것처럼 보이는 몇 가지 질문을 보았지만 대답이 무엇을 의미하는지 잘 모르겠습니다.

이것은 내가 추구하는 것에 가장 가까운 것 같습니다 : asp-net-mvc-downloading-excel- 그러나 나는 응답을 이해하지 못하며 이제 몇 년이되었습니다. 또한 iframe을 사용하여 파일 다운로드를 처리하는 방법에 대한 다른 기사 (더 이상 찾을 수 없음)를 발견했지만 MVC에서이 작업을 수행하는 방법을 모르겠습니다.

전체 게시물을 다시 수행하는 경우 내 Excel 파일이 제대로 반환되지만 mvc에서 AJAX로 작업 할 수 없습니다.

답변:


215

AJAX 호출을 통해 다운로드 할 파일을 직접 반환 할 수 없으므로 다른 방법은 AJAX 호출을 사용하여 관련 데이터를 서버에 게시하는 것입니다. 그런 다음 서버 측 코드를 사용하여 Excel 파일을 만들 수 있습니다 (이 부분이 작동하는 것처럼 들리지만 EPPlus 또는 NPOI를 사용하는 것이 좋습니다).

2016 년 9 월 업데이트

내 원래 답변 (아래)은 3 년 이상되었으므로 AJAX를 통해 파일을 다운로드 할 때 더 이상 서버에 파일을 만들지 않기 때문에 업데이트 할 것이라고 생각했지만 여전히 사용에 따라 원래 답변을 남겼습니다. 귀하의 특정 요구 사항.

내 MVC 애플리케이션의 일반적인 시나리오는 사용자가 구성한 보고서 매개 변수 (날짜 범위, 필터 등)가있는 웹 페이지를 통해보고하는 것입니다. 사용자가 서버에 게시하는 매개 변수를 지정하면 보고서가 생성되고 (예 : Excel 파일을 출력으로) 결과 파일을 TempData고유 한 참조 가있는 버킷에 바이트 배열로 저장합니다 . 이 참조는 TempData최종 사용자 브라우저 에서 데이터를 추출 하고 다운로드하기 위해 별도의 컨트롤러 작업으로 리디렉션되는 내 AJAX 함수에 Json 결과로 다시 전달됩니다 .

좀 더 자세히 설명하기 위해 Model 클래스에 바인딩 된 폼이있는 MVC 뷰가 있다고 가정하고 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" }
   };

}

내 MVC 양식을 위의 컨트롤러에 게시하고 응답을받는 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(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것보다 사용의 장점은 데이터가 지워 지므로 많은 양의 파일 요청이있는 경우 메모리 사용량 측면에서 더 효율적이라는 것입니다. TempData 모범 사례를 참조하십시오 .SessionTempData

원래 답변

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 파일을 생성하는 Controller / Action 메서드입니다.
  • 데이터 매개 변수에는 양식에서 추출되는 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
이것은 좋은 잠재적 인 옵션처럼 보이지만 계속 진행하기 전에 먼저 서버에서 파일을 만드는 것과 관련이없는 다른 대안이 없습니까?
Valuk

4
내가 아는 것은 아닙니다.이 접근 방식은 여러 번 성공적으로 사용했습니다. 사용자의 관점에서 볼 때 매끄 럽기 때문에 유일하게 염두에 두어야 할 것은 시간이 지남에 따라 파일이 마운트 될 때 생성되는 파일을 정리하기 위해 하우스 키핑 루틴이 필요하다는 것입니다.
connectedsoftware

7
엔드 포인트 생성 '/ Download? file = ...'SCREAMS 대규모 보안 위험-저는 보안 전문가는 아니지만 사용자 인증, 입력 위생, MVC의 [ValidateAntiForgeryToken]을 추가하고 다른 보안을 가장 잘 언급하고 싶습니다. -이 답변에 대한 연습.
Jimmy

2
@CSL 항상 오류 0x800a03f6-JavaScript 런타임 오류 : var response = JSON.parse (data);
Standage

2
좋습니다. 이전 답변을 맨 아래에 두지 그래요? 그리고 상단의 새로운 답변, 그래서 사람들은 시간을 낭비하지 않습니다
goamn

19

내 2 센트-엑셀을 서버에 물리적 파일로 저장할 필요가 없습니다. 대신 (세션) 캐시에 저장하십시오. 캐시 변수 (해당 엑셀 파일을 저장하는)에 대해 고유하게 생성 된 이름을 사용하십시오. 이것은 (초기) ajax 호출의 반환이 될 것입니다. 이렇게하면 파일 액세스 문제를 처리 할 필요가없고 필요하지 않을 때 파일을 관리 (삭제) 할 필요가 없으며 파일을 캐시에 저장하면 검색 속도가 더 빠릅니다.


1
정확히 어떻게 하시겠습니까? 흥미 롭 네요.
Natalia는

2
예가 좋을 것입니다 (엑셀 파일을 생성하지 않고 캐시에 저장하는 방법을 의미합니다).
Tadej 2011

그래도 얼마나 확장 가능합니까? 사용자가 여러 개의 큰 보고서를 다운로드하는 경우?
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);
    }
}

그리고 마지막으로 MVC Razor보기에서 ajax 호출

//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 스토리지와 세션 중에 내보내는 파일 수 및 사용자가 많은 경우에 따라 많은 메모리 / 공간을 소비 할 수 있습니다.

대신 TempData를 사용하도록 CSL에서 서버 측 코드를 업데이트했습니다.

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) {debugger; var bytes = new Uint8Array (Rdata.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 (); },
GVKRAO

일부는 위의 링크에서 Excel 파일 다운로드를 구현했습니다. @ html.Beginform ()에서만 작동합니다. 그런 다음 약간의 변경이 필요한 경우 해당 코드가 필요합니다. AJAX 호출 성공 블록의 경우 확인하십시오. AJAX CALL
GVKRAO

3
$ .ajax ({
                유형 : "GET",
                URL : "/ Home / Downloadexcel /",
                contentType : "application / json; charset = utf-8",
                데이터 : null,
                성공 : function (Rdata) {
                    디버거;
                    var bytes = new Uint8Array (Rdata.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 ();
                },
                오류 : function (err) {

                }

            });

1

모든 것이 컨트롤러에서 정상적으로 반환되는 것처럼 보였음에도 불구하고 ajax 호출에서 502 Bad Gateway 결과를 얻었으므로 받아 들인 대답은 저에게 효과적이지 않았습니다 .

아마도 TempData로 한계에 도달하고 있었을 것입니다-확실하지 않지만 TempData 대신 IMemoryCache 를 사용하면 정상적으로 작동 한다는 것을 알았 습니다. 그래서 여기에 허용 된 대답에 코드의 수정 된 버전이 있습니다.

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

Startup.cs의 ConfigureServices에 다음이 있는지 확인하십시오.

services.AddDistributedMemoryCache();

0

이 스레드는 제가 여기서 공유 할 솔루션을 만드는 데 도움이되었습니다. 처음에는 문제없이 GET ajax 요청을 사용했지만 요청 URL 길이가 초과 된 지점에 도달했기 때문에 POST에 연결해야했습니다.

자바 스크립트는 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);
}

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

파일을 열 수 없으며 엑셀이 열리고 닫히지 않고 robj 직전에 stream.close ()를 추가했지만 작동하지 않습니다.
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 의 일부) 을 게시하고있는 것 같습니다 . 이는 a를 반환 하고 이것은 ..를 반환합니다 . 그리고 필터 값을 게시하기 위해 말했듯이, ( 필요한 경우), 설명하려고 시도한대로 다른 작업에 대한 게시 요청을합니다.Action methodFileResultFileResultthe 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();
        }
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.