Tải xuống tệp Excel qua AJAX MVC


92

Tôi có một biểu mẫu (ish) lớn trong MVC.

Tôi cần có thể tạo tệp excel chứa dữ liệu từ một tập hợp con của biểu mẫu đó.

Một chút khó khăn là điều này sẽ không ảnh hưởng đến phần còn lại của biểu mẫu và vì vậy tôi muốn thực hiện nó thông qua AJAX. Tôi đã xem qua một số câu hỏi trên SO có vẻ liên quan, nhưng tôi không thể tìm ra câu trả lời có nghĩa là gì.

Câu trả lời này có vẻ gần nhất với những gì tôi đang theo đuổi: asp-net-mvc-download-excel - nhưng tôi không chắc mình hiểu câu trả lời và hiện nó đã được vài năm. Tôi cũng đã xem một bài viết khác (không tìm thấy nữa) về việc sử dụng iframe để xử lý tải xuống tệp, nhưng tôi không chắc chắn làm thế nào để điều này hoạt động với MVC.

Tệp excel của tôi trả về tốt nếu tôi đang thực hiện một bài đăng đầy đủ nhưng tôi không thể làm cho nó hoạt động với AJAX trong mvc.

Câu trả lời:


215

Bạn không thể trực tiếp trả lại tệp để tải xuống thông qua lệnh gọi AJAX, vì vậy, một phương pháp thay thế là sử dụng lệnh gọi AJAX để đăng dữ liệu liên quan lên máy chủ của bạn. Sau đó, bạn có thể sử dụng mã phía máy chủ để tạo Tệp Excel (Tôi khuyên bạn nên sử dụng EPPlus hoặc NPOI cho việc này mặc dù có vẻ như bạn có phần này đang hoạt động).

CẬP NHẬT Tháng 9 năm 2016

Câu trả lời ban đầu của tôi (bên dưới) đã được hơn 3 năm, vì vậy tôi nghĩ rằng tôi sẽ cập nhật vì tôi không còn tạo tệp trên máy chủ khi tải xuống tệp qua AJAX. Tuy nhiên, tôi đã để lại câu trả lời ban đầu vì nó có thể còn được sử dụng tùy thuộc vào yêu cầu cụ thể của bạn.

Một tình huống phổ biến trong các ứng dụng MVC của tôi là báo cáo qua một trang web có một số thông số báo cáo do người dùng định cấu hình (Phạm vi ngày, Bộ lọc, v.v.). Khi người dùng đã chỉ định các tham số mà họ đăng lên máy chủ, báo cáo sẽ được tạo (ví dụ: tệp Excel làm đầu ra) và sau đó tôi lưu trữ tệp kết quả dưới dạng mảng byte trong TempDatanhóm với một tham chiếu duy nhất. Tham chiếu này được chuyển trở lại dưới dạng Kết quả Json cho hàm AJAX của tôi, sau đó chuyển hướng đến hành động của bộ điều khiển riêng biệt để trích xuất dữ liệu từ đó TempDatavà tải xuống trình duyệt của người dùng cuối.

Để cung cấp thêm chi tiết này, giả sử bạn có Chế độ xem MVC có biểu mẫu liên kết với một lớp Mô hình, hãy gọi Mô hình ReportVM.

Đầu tiên, hành động của bộ điều khiển là bắt buộc để nhận được mô hình đã đăng, một ví dụ sẽ là:

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

}

Lệnh gọi AJAX đăng biểu mẫu MVC của tôi lên bộ điều khiển ở trên và nhận được phản hồi giống như sau:

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

Hành động của trình điều khiển để xử lý việc tải xuống tệp:

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

Một thay đổi khác có thể dễ dàng được thực hiện nếu được yêu cầu là chuyển Loại MIME của tệp làm tham số thứ ba để một hành động Bộ điều khiển có thể phân phát chính xác nhiều định dạng tệp đầu ra.

Điều này loại bỏ bất kỳ nhu cầu nào đối với bất kỳ tệp vật lý nào được tạo và lưu trữ trên máy chủ, do đó, không yêu cầu quy trình vệ sinh và một lần nữa, điều này là liền mạch với người dùng cuối.

Lưu ý, lợi thế của việc sử dụng TempDatachứ không phải Sessionlà một khi TempDatalà đọc dữ liệu sẽ bị xóa vì vậy nó sẽ có hiệu quả hơn về mặt sử dụng bộ nhớ nếu bạn có một số lượng lớn các yêu cầu tập tin. Xem Thực hành tốt nhất về TempData .

Câu trả lời GỐC

Bạn không thể trực tiếp trả lại tệp để tải xuống thông qua lệnh gọi AJAX, vì vậy, một phương pháp thay thế là sử dụng lệnh gọi AJAX để đăng dữ liệu liên quan lên máy chủ của bạn. Sau đó, bạn có thể sử dụng mã phía máy chủ để tạo Tệp Excel (Tôi khuyên bạn nên sử dụng EPPlus hoặc NPOI cho việc này mặc dù có vẻ như bạn có phần này đang hoạt động).

Khi tệp đã được tạo trên máy chủ, hãy chuyển lại đường dẫn đến tệp (hoặc chỉ tên tệp) làm giá trị trả về cho lệnh gọi AJAX của bạn và sau đó đặt JavaScript window.locationthành URL này sẽ nhắc trình duyệt tải xuống tệp.

Từ góc độ người dùng cuối, hoạt động tải xuống tệp diễn ra liền mạch vì họ không bao giờ rời khỏi trang bắt nguồn yêu cầu.

Dưới đây là một ví dụ đơn giản theo mô hình của một lệnh gọi ajax để đạt được điều này:

$.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;
    }
});
  • tham số url là phương thức Bộ điều khiển / Hành động nơi mã của bạn sẽ tạo tệp Excel.
  • tham số dữ liệu chứa dữ liệu json sẽ được trích xuất từ ​​biểu mẫu.
  • returnValue sẽ là tên tệp của tệp Excel mới được tạo của bạn.
  • Lệnh window.location chuyển hướng đến phương thức Controller / Action thực sự trả về tệp của bạn để tải xuống.

Phương thức bộ điều khiển mẫu cho hành động Tải xuống sẽ là:

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

3
Đây có vẻ là một tùy chọn tiềm năng tốt, nhưng trước khi tôi tiếp tục, không có lựa chọn thay thế nào khác không liên quan đến việc tạo tệp trên máy chủ trước tiên?
Valuk

4
Không phải tôi biết - cách tiếp cận này tôi đã sử dụng thành công nhiều lần. Từ quan điểm của người dùng, nó là liền mạch, điều duy nhất cần lưu ý là bạn sẽ cần một thói quen dọn dẹp nhà cửa để dọn dẹp các tệp được tạo vì chúng sẽ tăng lên theo thời gian.
kết nối

7
Tạo điểm cuối '/ Download? File = ...' Rủi ro bảo mật lớn LỪA ĐẢO - Tôi không phải là chuyên gia bảo mật nhưng tôi nghĩ bạn muốn thêm xác thực người dùng, vệ sinh đầu vào, [ValidateAntiForgeryToken] của MVC và đề cập đến các bảo mật tốt nhất khác -practices cho câu trả lời này.
Jimmy

2
@CSL Tôi luôn gặp lỗi 0x800a03f6 - Lỗi thời gian chạy JavaScript: Ký tự không hợp lệ trên var response = JSON.parse (data);
Standage

2
Tuyệt vời, tại sao bạn không đặt câu trả lời cũ ở dưới cùng? Và câu trả lời mới ở trên cùng, vì vậy mọi người đừng lãng phí thời gian
goamn 14/1117

19

2 xu của tôi - bạn không cần phải lưu trữ excel dưới dạng tệp vật lý trên máy chủ - thay vào đó, hãy lưu trữ nó trong Bộ nhớ đệm (Phiên). Sử dụng tên được tạo duy nhất cho biến Cache của bạn (lưu trữ tệp excel đó) - đây sẽ là giá trị trả về của lệnh gọi ajax (ban đầu) của bạn. Bằng cách này, bạn không phải giải quyết các vấn đề truy cập tệp, quản lý (xóa) tệp khi không cần thiết, v.v. và, có tệp trong Bộ nhớ cache, việc truy xuất tệp sẽ nhanh hơn.


1
Làm thế nào chính xác bạn sẽ làm điều đó? Nghe có vẻ thú vị.
Natalia

2
Một ví dụ sẽ rất hay (ý tôi là làm thế nào để lưu trữ nó trong bộ nhớ cache, không tạo ra tệp excel).
Tadej

Làm thế nào có thể mở rộng là điều này mặc dù? Nếu người dùng đang tải xuống một số báo cáo lớn?
Zapnologica

Nếu bạn đang sử dụng Azure, phiên sẽ hoạt động cho đến khi bạn TẮT ARRAffinity.
JeeShen Lee

14

Gần đây tôi đã có thể thực hiện điều này trong MVC (mặc dù không cần sử dụng AJAX) mà không cần tạo tệp vật lý và nghĩ rằng tôi muốn chia sẻ mã của mình:

Hàm JavaScript siêu đơn giản (nhấp vào nút datatables.net kích hoạt điều này):

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

Mã bộ điều khiển 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);
    }

Trong lớp ExportHelper, tôi sử dụng một công cụ của bên thứ 3 ( GemBox.S Spreadsheet ) để tạo tệp Excel và nó có tùy chọn Lưu vào Dòng. Nói như vậy, có một số cách để tạo tệp Excel có thể dễ dàng ghi vào luồng bộ nhớ.

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

     }
}

Trong IE, Chrome và Firefox, trình duyệt nhắc tải xuống tệp và không có điều hướng thực sự nào xảy ra.


8

Đầu tiên Tạo hành động bộ điều khiển sẽ tạo Tệp 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 = "" });
}

sau đó tạo hành động Tải xuống

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

Nếu bạn muốn xóa tệp sau khi tải xuống, hãy tạo tệp này

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

và cuối cùng là cuộc gọi ajax từ bạn xem 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

Tôi đã sử dụng giải pháp được đăng bởi CSL nhưng tôi khuyên bạn không nên lưu trữ dữ liệu tệp trong Phiên trong toàn bộ phiên. Bằng cách sử dụng TempData, dữ liệu tệp sẽ tự động bị xóa sau yêu cầu tiếp theo (là yêu cầu GET cho tệp). Bạn cũng có thể quản lý việc xóa dữ liệu tệp trong Session trong hành động tải xuống.

Phiên có thể tiêu tốn nhiều bộ nhớ / không gian tùy thuộc vào bộ nhớ SessionState và số lượng tệp được xuất trong phiên và nếu bạn có nhiều người dùng.

Tôi đã cập nhật mã phía người tìm kiếm từ CSL để sử dụng TempData thay thế.

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 Tôi cũng đã bắt đầu sử dụng TempData, câu trả lời của bạn đã thúc đẩy tôi cập nhật bản cập nhật của tôi để phản ánh điều này!
kết nối

5

bằng cách sử dụng 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);
    }


Trong Khối thành công AJAX CALL, thành công: function (Rdata) {debugger; var bytes = new Uint8Array (Rdata.FileContents); var blob = new Blob ([byte], {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

một số người Đã triển khai tải xuống tệp Excel trong liên kết ở trên, nó chỉ hoạt động cho @ html.Beginform () sau đó sau khi có những thay đổi nhỏ cần mã đó, đối với AJAX cuộc gọi Khối thành công, Vui lòng kiểm tra nó, nó hoạt động tốt trong AJAX CALL
GVKRAO

3
$ .ajax ({
                gõ: "GET",
                url: "/ Home / Downloadexcel /",
                contentType: "application / json; charset = utf-8",
                dữ liệu: null,
                thành công: hàm (Rdata) {
                    trình gỡ lỗi;
                    var bytes = new Uint8Array (Rdata.FileContents); 
                    var blob = new Blob ([byte], {type: "application / vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
                    var link = document.createElement ('a');
                    link.href = window.URL.createObjectURL (blob);
                    link.download = "myFileName.xlsx";
                    link.click ();
                },
                error: function (err) {

                }

            });

1

Câu trả lời được chấp nhận không hoàn toàn phù hợp với tôi vì tôi nhận được kết quả 502 Bad Gateway từ cuộc gọi ajax mặc dù mọi thứ dường như đang trở lại tốt từ bộ điều khiển.

Có lẽ tôi đã đạt đến giới hạn với TempData - không chắc chắn, nhưng tôi thấy rằng nếu tôi sử dụng IMemoryCache thay vì TempData , nó hoạt động tốt, vì vậy đây là phiên bản mã đã điều chỉnh của tôi trong câu trả lời được chấp nhận:

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

}

Cuộc gọi AJAX vẫn như với câu trả lời được chấp nhận (tôi không thực hiện thay đổi nào):

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

Hành động của trình điều khiển để xử lý việc tải xuống tệp:

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

...

Bây giờ có một số mã bổ sung để thiết lập MemoryCache ...

Để sử dụng "_cache", tôi đã chèn hàm tạo cho bộ điều khiển như sau:

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

Và đảm bảo rằng bạn có những thứ sau trong ConfigureServices trong Startup.cs:

services.AddDistributedMemoryCache();

0

Chủ đề này đã giúp tôi tạo ra giải pháp của riêng mình mà tôi sẽ chia sẻ ở đây. Lúc đầu, tôi đã sử dụng yêu cầu GET ajax mà không có vấn đề gì nhưng nó đã đến một điểm mà độ dài URL yêu cầu bị vượt quá nên tôi phải chuyển sang ĐĂNG.

Javascript sử dụng plugin tải xuống tệp JQuery và bao gồm 2 lệnh gọi tiếp theo. Một ĐĂNG (Để gửi thông số) và một ĐĂNG để truy xuất tệp.

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

Phía máy chủ

    [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

Câu trả lời của CSL đã được triển khai trong một dự án mà tôi đang thực hiện nhưng vấn đề tôi gặp phải là mở rộng quy mô trên Azure đã làm hỏng quá trình tải xuống tệp của chúng tôi. Thay vào đó, tôi có thể thực hiện việc này bằng một lệnh gọi AJAX:

NGƯỜI PHỤC VỤ

[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 (phiên bản sửa đổi của tệp Xử lý tải xuống từ bài đăng 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);
    }

không thể mở tệp, excel chỉ mở và sau đó không tự đóng lại, tôi thậm chí đã thêm stream.close () ngay trước robj nhưng không hoạt động.
rạng sáng

0

Tôi nghe có vẻ khá ngây thơ và có thể bị chỉ trích khá nhiều, nhưng đây là cách tôi đã làm điều đó,
( Nó không liên quan đến ajaxxuất khẩu, nhưng nó cũng không đăng lại đầy đủ )

Cám ơn này bưu chính này câu trả lời.
Tạo một bộ điều khiển đơn giản

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

Và đây là các Chế độ xem ..

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

Toàn bộ điểm của thủ thuật dường như là, chúng tôi đang đăng một biểu mẫu (một phần của Chế độ xem Razor) mà chúng tôi đang gọi là một Action method, nó trả về: a FileResult, và điều này FileResulttrả về the Excel File..
Và để đăng các giá trị bộ lọc, như đã nói, ( và nếu bạn yêu cầu), tôi đang thực hiện một yêu cầu đăng đối với một hành động khác, như đã được cố gắng mô tả ..


-1

Tôi đang sử dụng Asp.Net WebForm và chỉ muốn tải xuống một tệp từ phía máy chủ. Có rất nhiều bài báo nhưng tôi không thể tìm thấy câu trả lời chỉ cơ bản. Bây giờ, tôi đã thử một cách cơ bản và đã hiểu.

Đó là vấn đề của tôi.

Tôi phải tạo nhiều nút đầu vào động trong thời gian chạy. Và tôi muốn thêm từng nút để tải xuống với việc cung cấp một Số tệp duy nhất.

Tôi tạo mỗi nút như thế này:

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

Mỗi nút gọi phương thức ajax này.

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

Sau đó, tôi đã viết một phương pháp đơn giản cơ bản.

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

Tôi đang tạo Biểu mẫu_1, Biểu mẫu_2, Biểu mẫu_3 này .... Và tôi sẽ xóa các tệp cũ này bằng một chương trình khác. Nhưng nếu có một cách để chỉ cần gửi mảng byte để tải xuống tệp như sử dụng Response. Tôi muốn sử dụng nó.

Tôi hy vọng điều này sẽ hữu ích cho bất kỳ ai.


-1

Trên biểu mẫu Gửi

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();
        }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.