简体   繁体   中英

How do I generate and return multiple files with additional data to Razor views using ASP.NET MVC?

My Controller/Action receives some Ids from the view (via Post).

For each of the Ids a file will be fetched. I need to return some generated Excel or PDF files along the the list of IDs that did not fetch any results. Users can download any of the generated files.

I have all the logic in place to build the list of IDs that failed and the Excel and PDF reports, but am not sure how to return this back to the view. Appreciate any help with this.

The flow:

  1. Accept list of IDs from the view (done)
  2. Fetch results for these IDs (done)
  3. Build a list of IDs that did not fetch data (done), a report for the IDs that fetched data in Excel format (done) and a report for the IDs that fetched data in PDF format (done)
  4. Return the artifacts built in step 3 (need help with this)

Post can be both Ajax and non-ajax. I am guessing you are not doing Ajax post.

Sending the files from the controller to the view is easier. However how do you plan to deliver the file based on users choice? View consists of Html, CSS and javascript. So you have to figure out a way to serve the binaray data that you are holding.

Since you are keeping the files in the memory then why don't you send the files as binary data to the view?

Here is a code example how you can send the files to the view:

Models:

public class MyModel
{
    Public List<MyFile> files { get; post; }
    
    Public int TotalFilesProcessed { get; post; }
    Public int TotalFilesFailed { get; post; }
}

public class MyFile
{
    Public string FileName { get; post; }
    Public string FileExtension { get; post; }
    Public byte[] FileContent { get; post; }
    Public double FileLength { get; post; }
    Public double ContentType { get; post; } 
}

Controller/Action:

MyModel model = new MyModel();
MyModel.files = new List<MyFile>();

MyFile file1 = new MyFile();
file1.FileExtension = "pdf"
file1.FileContent = null; //Assign file byte from the memory
file1.FileLength = file1.FileContent.Length;
file1.ContentType = "application/octet-stream";
MyModel.files.Add(file1);

MyFile file2 = new MyFile();
file2.FileExtension = "png"
file2.FileContent = null; //Assign file byte from the memory
file2.FileLength = file2.FileContent.Length;
file2.ContentType = "application/octet-stream";
MyModel.files.Add(file2);

MyModel.TotalFilesProcessed = 5;
MyModel.TotalFilesFailed = 2;

return View(model);

On the razor view you can write C# codes to get the data from the model. Loop through the List<MyFile> and render the file names as Html buttons or hyperlinks so that user can click one and download the file.

As per your question that is how you can have the files sent to the view. But the actual question should be how do you transmit the byte to the user when user clicks a button.

Following is a piece of code that will help you to put the bytes within the javascript code. Read the details of the code here :

var bytes = new Uint8Array(resultByte); // pass your byte response to this constructor

var blob=new Blob([bytes], {type: "application/pdf"});// change resultByte to bytes

var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="myFileName.pdf";
link.click();

The concept here is to transmit the byte from the html page so that the browser triggers a file download for the content. You will need to tweak the js as per your needs.

Note:

I think preparing the files in advance and holding in the memory is not a good idea.

Instead why don't you prepare a partial action which can be called from the page (javascript) with parameters. Then this partial action will prepare a file based on your input and send the byte/status to the view.

Based on status you can decide if the file can be allowed to download or a status message will be displayed instead.

Here is a recomended flow:

  • Have a partial action which takes one Id and process the file for that. If no data is found then send a 404 json result.
  • For all the Ids call the partial action in a loop and gather all the response in your jabascript
  • Once you have all the results (or even as each of the ajax response comes in) you can add a button/hyperlink for that in the UI.
  • Display a loader on the UI so that user know files are being processed. You can choose to block the UI till all the files are processed on allow the user to download as soon as a file becomes available

Doing the above will not need any cleanup job or hold larger number of files in the memory.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM