programing

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

goodsources 2023. 5. 11. 21:26
반응형

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

저는 MVC에 큰 (아이시) 양식을 가지고 있습니다.

저는 그 양식의 하위 집합에서 데이터가 포함된 엑셀 파일을 생성할 수 있어야 합니다.

까다로운 점은 이것이 나머지 양식에 영향을 미치지 않아야 한다는 것입니다. 그래서 저는 AJAX를 통해 이 작업을 수행하고 싶습니다.SO와 관련된 것처럼 보이는 몇 가지 질문을 발견했지만, 답변이 무엇을 의미하는지 잘 모르겠습니다.

이것은 제가 추구하는 것에 가장 가까운 것 같습니다: asp-net-mvc-downloading-excel - 하지만 저는 그 반응을 이해할 수 있는지 확신할 수 없고, 그것은 지금 몇 년이 되었습니다.iframe을 사용하여 파일 다운로드를 처리하는 방법에 대한 다른 문서를 발견했지만(더 이상 찾을 수 없습니다) MVC에서 이 작업을 수행하는 방법을 잘 모르겠습니다.

내 엑셀 파일은 내가 전체 포스트백을 하고 있다면 정상적으로 반환되지만, 나는 그것을 mvc의 AJAX와 함께 작동시킬 수 없습니다.

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

업데이트 2016년 9월

저의 원래 답변(아래)은 3년이 넘었기 때문에 AJAX를 통해 파일을 다운로드 할 때 더 이상 서버에 파일을 생성하지 않기 때문에 업데이트를 하려고 생각했지만, 당신의 구체적인 요구 사항에 따라 여전히 유용할 수 있기 때문에 원본 답변을 남겨두었습니다.

MVC 응용 프로그램의 일반적인 시나리오는 일부 사용자가 구성한 보고서 매개 변수(날짜 범위, 필터 등)가 있는 웹 페이지를 통해 보고하는 것입니다. 변수를 예: ), 을 바이트 로 "" " " " " " " " (" " " Excel " (" " " " " " " 에 합니다.TempData고유한 참조가 있는 버킷입니다.에 별도의 데이터를 전달됩니다. Json Result 기능은 "Json Result" 및 "AJAX"에서 를 추출합니다.TempData최종 사용자 브라우저로 다운로드할 수 있습니다.

이 있는 라고 부릅니다.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 유형을 세 번째 매개변수로 전달하여 하나의 컨트롤러 작업이 다양한 출력 파일 형식을 올바르게 제공할 수 있도록 하는 것입니다.

따라서 물리적 파일을 생성하고 서버에 저장할 필요가 없으므로 하우스키핑 루틴이 필요하지 않으며 최종 사용자도 이 작업을 원활하게 수행할 수 있습니다.

를 사용할 때의 장점: 이의점사.TempDataSession한 번뿐인TempData데이터가 지워지므로 파일 요청이 많은 경우 메모리 사용량 측면에서 더 효율적입니다.온도 데이터 모범 사례를 참조하십시오.

원본 답변

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

파일 를 값으로 JavaScript 파또일로경를의(JavaScript 이만름파를일되는다면설일성니)호출파대(JavaScript 정합다음전한)합니다.window.location하도록 요청할 수 있습니다. " " URL"은 파일이름입니다.

최종 사용자의 관점에서 파일 다운로드 작업은 요청이 발생한 페이지를 떠나지 않기 때문에 원활합니다.

다음은 이를 달성하기 위해 고안된 간단한 Ajax 호출의 예입니다.

$.ajax({
    type: 'POST',
    url: '/Reports/ExportMyData', 
    data: '{ "dataprop1": "test", "dataprop2" : "test2" }',
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    success: function (returnValue) {
        window.location = '/Reports/Download?file=' + returnValue;
    }
});
  • url 매개 변수는 코드가 Excel 파일을 생성할 컨트롤러/작업 메서드입니다.
  • data 매개변수에는 폼에서 추출될 json 데이터가 포함되어 있습니다.
  • returnValue는 새로 생성한 Excel 파일의 파일 이름입니다.
  • window.location 명령은 실제로 다운로드할 파일을 반환하는 Controller/Action 메서드로 리디렉션합니다.

다운로드 작업에 대한 샘플 컨트롤러 방법은 다음과 같습니다.

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

제 2센트는 엑셀을 서버에 실제 파일로 저장할 필요가 없습니다. 대신 (세션) 캐시에 저장하십시오.캐시 변수(Excel 파일을 저장하는)에 대해 고유하게 생성된 이름을 사용합니다. 이것은 (초기) Ajax 호출의 반환입니다.이렇게 하면 파일 액세스 문제, 필요하지 않을 때 파일을 관리(삭제)하는 등의 문제를 처리할 필요가 없으며 캐시에 파일이 있으면 파일을 검색하는 속도가 빨라집니다.

최근에 물리적 파일을 생성하지 않고도 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)를 사용합니다.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에서는 브라우저가 파일을 다운로드하라는 메시지를 표시하지만 실제 탐색은 수행되지 않습니다.

먼저 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 레이저 뷰에서 아약스 전화가 왔습니다.

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

CSL에서 게시한 솔루션을 사용했지만 전체 세션 중에는 파일 데이터를 세션에 저장하지 않는 것이 좋습니다.TempData를 사용하면 다음 요청(파일에 대한 GET 요청) 후 파일 데이터가 자동으로 제거됩니다.다운로드 작업에서 세션의 파일 데이터 제거를 관리할 수도 있습니다.

세션은 세션 상태 저장소, 세션 중에 내보내는 파일 수 및 사용자가 많은 경우에 따라 많은 메모리/공간을 사용할 수 있습니다.

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

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);
    }
$.580달러유형: "GET",url: "/홈/다운로드 엑셀/",내용물유형: "application/json; charset=utf-8",데이터: null,성공: 함수(Rdata) {디버거;varbytes = 새 Uint8Array(Rdata.파일 내용);varblob = newBlob([bytes]), {type: "application/vnd.openxmlformats-officedocument.sysetml.시트" });varlink = document.createElement('a');
링크를 걸다href = 창.URL.createObjectURL(blob);
link.dll = "myFileName.xlsx";
link.click();},오류: 함수(err) {
}
});

컨트롤러에서 모든 것이 정상적으로 돌아오고 있는 것처럼 보였음에도 불구하고 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
    }
}

...

이제 메모리 캐시를 설정하기 위한 추가 코드가 있습니다...

"_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 의 서비스 구성에서 다음을 확인합니다.

services.AddDistributedMemoryCache();
  $.ajax({
    global: false,
    url: SitePath + "/User/ExportTeamMembersInExcel",
    "data": { 'UserName': UserName, 'RoleId': RoleId, UserIds: AppraseeId },
    "type": "POST",
    "dataType": "JSON",
   "success": function (result) {
        
        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);
    }

순진하게 들릴 수도 있고, 비난을 받을 수도 있지만, 제가 한 방법은 이렇습니다.
(수출에는 포함되지 않지만 전체 포스트백도 수행하지 않습니다.

게시물과 이 답변에 감사드립니다.
단순 컨트롤러 생성

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

속임수의 요점은 우리가 그 위에 부르는 양식(레이저 뷰의 일부)을 게시하고 있다는 것입니다.Action method를 반환하는 경우: a, 그리고 이것은 …을 반환합니다.
그리고 말씀하신 대로 필터 값을 게시하기 위해 (그리고 필요하다면) 다른 작업에 게시 요청을 하고 있습니다.

이 스레드는 여기서 공유할 나만의 솔루션을 만드는 데 도움이 되었습니다.처음에는 GET agjax 요청을 문제없이 사용하고 있었는데 요청 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");
    }

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

클라이언트(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));
});

이것은 나에게 효과가 있습니다.컨트롤러 작업에서 내용이 포함된 파일을 반환해야 합니다."application/vnd.openxml formats-officedocument.spreadsheetml"로 입력합니다.sheet" 및 파일 이름(예: "List.xlsx")은 AJAX 성공 호출과 동일해야 합니다.ClosedX를 사용한 적이 있습니다.Excel 파일을 생성하기 위한 ML NuGet 패키지입니다.

$.ajax({
    url: "Home/Export",
    type: 'GET',      
    contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    xhrFields: { responseType: 'blob' },
    success: function (data) {
        var a = document.createElement('a');
        var url = window.URL.createObjectURL(data);
        a.href = url;
        a.download = 'List.xlsx';
        a.click();
        window.URL.revokeObjectURL(url);
    }
});

저는 Asp를 사용하고 있습니다.NetWebForm과 서버 측에서 파일을 다운로드하고 싶습니다.기사는 많은데 기본적인 답만 못 찾겠어요.저는 기본적인 방법을 시도했고 그것을 얻었습니다.

그게 내 문제야.

저는 런타임에 동적으로 많은 입력 버튼을 만들어야 합니다.그리고 각 버튼마다 다운로드 버튼을 추가하여 고유한 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을 생성합니다.그리고 이 오래된 파일들을 다른 프로그램으로 삭제하려고 합니다.하지만 응답을 사용하는 것처럼 파일을 다운로드하기 위해 바이트 배열을 보내는 방법이 있다면,쓰고 싶어요.

이것이 누구에게나 유용하기를 바랍니다.

제출 시 양식

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

언급URL : https://stackoverflow.com/questions/16670209/download-excel-file-via-ajax-mvc

반응형