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:
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:
Doing the above will not need any cleanup job or hold larger number of files in the memory.