Open PDF reusing code - c#

How to open pdf in my asp.net mvc website? But reusing code and which I have not. I do not want to do this for each pdf, so I want to reuse code.
My Index View:
<p>#Html.ActionLink("Historia.pdf", "ObtenerPdf", "Home")</p>
My Controller Method:
public FileStreamResult ObtenerPdf()
{
FileStream fs = new FileStream("c:\\Historia.pdf", FileMode.Open, FileAccess.Read);
return File(fs, "application/pdf");
}

Untested, I don't have VS in front of me to check.
In summary, you need to add a parameter to your DownloadPdf action and pass it in with the action link.
Here's some code to list all the PDFs (Q used C:\ so this does as well, see notes), show them to the user and download.
Controller:
public ActionResult Index()
{
var files = System.IO.Directory.GetFiles(#"C:\", "*.pdf")
.Select(x => System.IO.Path.GetFileNameWithoutExtension(x))
.ToList();
return View(files);
}
public FileStreamResult ObtenerPdf(string file)
{
FileStream fs = new FileStream("c:\\" + file + ".pdf", FileMode.Open, FileAccess.Read);
return File(fs, "application/pdf");
}
View:
#model IList<string>
#foreach (var file in Model)
{
<p>#Html.ActionLink(file, "ObtenerPdf", "Home", new { file }, null)</p>
}
Couple of notes:
Directory.GetFiles includes the path - there are other methods to get a list of files without the path included.
ActionLink overload is: text, action, controller, routeValues, attributes - without the null it will pick the wrong overload.
In new { file } if you don't specify a name, it will use the variable name, you could be specific to match the parameter name on the action which would be more robust, ie: new { file = file } but some optimisers will highlight this.
You probably don't want to list all your PDFs on C:\ on the server... you can use Server.MapPath to locate a folder within your application (then it doesn't matter if your site moves)
There's very likely an overload for return File to specify the path, so you don't need to open it first and will dispose it for you correctly etc, I've used the same code as supplied in the question.

Related

Asp.net Core File Upload

when i save image, image save successfully in database, but it takes full image path like this C:\Users....\Uploads\employee.jpg i dont want like this, i need to save image path somehting like this ~Uploads\employee.jpg and in specific folder and same path should save in database, also if someone show me after saving correct path how i can view that image. there is error i get because of this:
"Not allowed to load local resource :file:///C:/......"
thank you!
my code:
[HttpPost]
public async Task<IActionResult> Create(Photos photos)
{
if (ModelState.IsValid)
{
var filePath =
Path.Combine(_appEnvironment.ContentRootPath,
"Uploads", photos.FormFile.FileName);
photos.PhotoPath = filePath;
using (var stream = new FileStream(filePath, FileMode.Create))
{
await photos.FormFile.CopyToAsync(stream);
}
_context.Add(photos);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["NewsId"] = new SelectList(_context.News, "NewsId",
"NewsBigTitle", photos.NewsId);
return View(photos);
}
Break the code down:
var localPath = Path.Combine("Upload", "file.jpg");
var fullPath = Path.Combine(_appEnvironment.ContentRootPath, localPath);
Save the localPath to PhotoPath
Edit
Okey so now bring up your PhotoPath in a View, and make it target a file stream action.
[HttpGet("path/{image}")]
public FileStreamResult Image(string image)
{
var result = new FileStreamResult(new FileStream(
Path.Combine(_appEnvironment.ContentRootPath, image),
FileMode.Open,
FileAccess.Read), "image/<your_mime>");
return result;
}
The best way I think is to create a new string in the following format http://example.com/path/image.jpg and bind it to src.
You can't target dynamically added files by using the following: ~/path/image.jpg for your source.
Make sure you have configured IFileProvider pointing to your Uploads folder in Configure method of Startup class:
app.UseStaticFiles(); // Default one
// Adding one more location - folder named `Uploads` to be a custom static files folder.
// The `Uploads` folder is available in the content root directory of the application (where your `Controllers` folder.
// You can point it of course inside `wwwroot` - use `env.WebRootPath` instead)
app.UseStaticFiles(new StaticFileOptions {
FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "Uploads")),
RequestPath = new PathString("/Uploads")
});
Once you do this you should be able to upload the file this way in your controller action:
var filePath = Path.Combine(_environment.ContentRootPath, "Uploads", photos.FormFile.FileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await photos.FormFile.CopyToAsync(stream);
}

How to save a list of objects in a file in ASP.NET MVC

I am trying to save a list of objects (containing files URI) to a file in ASP.NET MVC so that when I load the page, the saved files URI will be loaded and displayed. When I tried it in Windows Forms, it works perfectly, however, I cannot get it working in MVC.
As a reference, the following is the code which I am using in Windows Forms to save the list of objects in a file and to load the contents of the file.
private void Save(List<Uri> list)
{
BinaryFormatter b = new BinaryFormatter();
FileStream file = File.Create(fileName);
b.Serialize(file, list.ToList());
file.Close();
}
private void LoadFile()
{
try
{
BinaryFormatter b = new BinaryFormatter();
FileStream file = File.Open(fileName, FileMode.Open);
fileList = (List<Uri>)b.Deserialize(file);
file.Close();
}
catch
{
MessageBox.Show("Error Loading File!");
}
}
When I put the same code in the Controller class, I am getting an error in the following lines:
FileStream file = File.Create(fileName);
FileStream file = File.Open(fileName, FileMode.Open);
Error:
'Controller.File(byte[], string)' is a method, which is not valid in the given context
My controller name is "FilesController" but I don't think that it is conflicting the names.
Any help would be very appreciated! :)
Thank you very much!
'Controller.File(byte[], string)' is a method, which is not valid in the given context
The Controller class has a member called File already. (A method, as the error states.) So when you do this in your controller:
File.Create(fileName);
The first reference to something called File is that method, which makes this line invalid. If you want to use the System.IO.File object, you have to specify that:
System.IO.File.Create(fileName);
Ideally such dependency-based operations wouldn't happen in a controller. But for simplicity it's not entirely uncommon to do these in a controller if the app doesn't do much in the first place.

How to upload multiple files to server with form post MVC?

I am working on a project of Asp.Net MVC-4. In my project user post requirements. Post contains title, tags, files etc.File may be multiple and it may be of any type like Video, doc ( ppt, excel, pdf, etc), images etc.
My problem is the handling of multiple file upload. Now first of all i tell you
currently how i am handling this :
I am using Jquery FIle Uplaod plugin. Through this plugin i am uploading file to server sequentially and on server i am saving those file with SessionId.Now when user post there requirement form than i just rename those file with userId.
drawback of my approach
First i have to save those files with session id and than i have to rename it with userId. So if i save my file in Window Azure Blobs than in that case for uploading single file i have to do 2 transaction. First save the blob with SessionId and than Renaming the blob with userid. Which i think result extra processing and extra cost.
Now i want to know if there is any approach by which i can upload all file (with progress bar for individual file [required]) with form post. So that user's requirement form (tags, titile etc) with all files go to server together than in that case i will save the user first in the database and than i will save the files with the userId ??
Note: I cannot save File with guid or other. UserId is required in the file name to uniquely identify user's files.
You can upload using following code in controller
[HttpPost]
public ActionResult Index(IEnumerable<HttpPostedFileBase> files) {
foreach (var file in files) {
if (file.ContentLength > 0) {
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
}
}
return RedirectToAction("Index");
}
more details here
Not sure if i completely understand the question, but you could post the model for your user details in the same POST as the file upload (same form?), then on the server:
[HttpPost]
public JsonResult AddNewImage(UserModel user)
{
ReturnArgs r = new ReturnArgs();
repo.AddUser(user) // add your user to DB here
SaveImages(user.Id);
r.Status = 200;
r.Message = "OK!";
return Json(r);
}
private void SaveImages(string userid)
{
for (var i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i] as HttpPostedFileBase;
string fileName = userid + "_" + i;
// saving file to DB here, but you can do what you want with
// the inputstream
repo.SaveImage(fileName, file.InputStream, file.ContentType);
}
}

How to download a file using FileResult?

I have a list in my view with an ActionLink button 'Download' and I want them to download a file when they click the link. The file is located in a map in my project.
View:
<div id="right-column-links">
<h2>Your active links</h2>
#if (lstLinks.Count == 0)
{
<p>You have no active links yet.</p>
}
else
{
<table>
#foreach (var item in lstLinks)
{
<tr>
<td>#Html.DisplayFor(model => item.Url)</td>
<td>#Html.ActionLink("Put inactive", "LinkInActive", new { linkid=item.LinkId }, new { onclick = "return confirm('Are you sure you want this link inactive?');" })</td>
<td>#Html.ActionLink("Download Qrcode", "DownloadQrcode", new { linkid=item.LinkId })</td>
</tr>
}
</table>
}
</div>
Controller:
[HttpPost]
public FileResult DownloadQrcode(int linkid)
{
Qrcode Qrcode = DbO.getQrcodebyLinkId(linkid);
string image = Server.MapPath("~") + "\\Qrcodes\\" + Qrcode.Image;
string contentType = "image/jpg";
return File(image, contentType, "Qrcode-" + Qrcode.QrcodeId);
}
The linkid comes from the selected link in the list. Then I lookup what qrcode matches the linkid in my database. From this qrcode object I get the image name. Example (qrcode-1337). Then I'am not sure what to do. I lookup the path where my project is stored and attach the map Qrcodes to it (where all the images are stored) and the image name. This returns me a link that he doesn't find.
Map location:
C:\Users\stage\Desktop\Immo-QR\Immo-QR\Immo-QR\Qrcodes
This doesn't seem to work. I am not sure how I should use FileResult. Can anyone explain this? Or show me another way?
EDIT:
A user suggested me to put the images in the App_Data file which I did under a map Qrcodes.
To save the file I use this code:
string path = Server.MapPath("~");
System.IO.File.WriteAllBytes(path + "\\App_Data\\Qrcodes\\qrcode-" + qrcodeid + ".jpg", bytes);
If I use "~\App_Data\Qrcodes\qrcode-" instead of the above, It doesn't work either.
I still get this error: Server Error in '/' Application. The resource cannot be found.
SOLUTION:
With this code it works!
public FileStreamResult DownloadQrcode(int linkid)
{
Qrcode Qrcode = DbO.getQrcodebyLinkId(linkid);
string path = Server.MapPath("~");
Stream image = new FileStream(path + "\\App_Data\\Qrcodes\\" + Qrcode.Image + ".jpg", FileMode.Open);
return File(image, "image/jpeg");
}
Try changing your string image line to Stream image.
This will help understand if you can't read the file. Your return File line will take a Stream with no issues.
Your approach is correct.
I think the path to the file is incorrect.
If you use ~\\Qrcodes\\filename it will translate to <appRootDirectory>\\QrCodes\\filename.
Also remember that IIS runs as a separate user in most cases, which does not have a home directory like a regular user.
I would suggest you move the Qrcodes to AppData folder or AppGlobalResources folder.
If you dont want to do that, you need to provide absolute path to Qrcodes folder.

How to render an ASP.NET MVC View in PDF format

I'm working with ExpertPDF's Html-to-PDF conversion utility for this question (although I'm open to other libraries if there's sufficient documentation).
In short, I have a view that is formatted a specific way and I would like to render it as a PDF document the user can save to disk.
What I have so far is a PrintService (which implements an IPrintService interface) and this implementation has two overloads for PrintToPDF(), one that takes just a URL and another that takes an HTML string, and both of which return a byte[]. I've only worked out the details of the second overload which requires the HTML string.
What I would like to do from my controller is something like:
public FileStreamResult Print(int id)
{
var model = _CustomRepository.Get(id);
string renderedView = SomethingThatRendersMyViewAsAString(model);
Stream byteStream = _PrintService.PrintToPdf(renderedView);
HttpContext.Response.AddHeader("content-disposition",
"attachment; filename=report.pdf");
return new FileStreamResult(byteStream, "application/pdf");
}
which in theory would render a PDF to the page. It's the "SomethingThatRendersMyViewAsAString" that I'm looking for help with. Is there a quick way to get the string representation of a View? Or perhaps I should just stick with the URL overload and pass in a URL to the view... Any other thoughts?
Thanks!
I packaged my solution in a Nuget package: Rotativa http://nuget.org/packages/Rotativa. It's based on wkhtmltopdf.
Usage is really simple.
Having an action you would like to serve as Pdf, instead of Html page. You can define an action that returns an ActionResult of the type ActionAsPdf (RouteAsPdf is also available).
So the code is just:
public ActionResult PrintIndex()
{
return new ActionAsPdf("Index", new { name = "Giorgio" }) { FileName = "Test.pdf" };
}
With name = "Giorgio" being a route parameter.
It works even if the action to print is protected by web forms authentication ([Authorize] attribute)
You might be able to tap into the Response during OnResultExecuting and replace the Filter property with something that stores the resultant HTML in a MemoryStream. Then you could clear the Response during OnResultExecuted and replace it with the results of your PDF conversion. I'm not sure that this would be better than just getting the HTML from the URL, though.
public FileStreamResult Print(int id)
{
var model = _CustomRepository.Get(id);
this.ConvertToPDF = true;
return View( "HtmlView" );
}
public override OnResultExecuting( ResultExecutingContext context )
{
if (this.ConvertToPDF)
{
this.PDFStream = new MemoryStream();
context.HttpContext.Response.Filter = new PDFStreamFilter( this.PDFStream );
}
}
public override OnResultExecuted( ResultExecutedContext context )
{
if (this.ConvertToPDF)
{
context.HttpContext.Response.Clear();
this.PDFStream.Seek( 0, SeekOrigin.Begin );
Stream byteStream = _PrintService.PrintToPDF( this.PDFStream );
StreamReader reader = new StreamReader( byteStream );
context.HttpContext.Response.AddHeader( "content-disposition",
"attachment; filename=report.pdf" );
context.HttpContext.Response.AddHeader( "content-type",
"application/pdf" );
context.HttpContext.Response.Write( reader.ReadToEnd() );
}
}
The PDFStreamFilter would need to override the "Write" method(s) and send the data to the memory stream instead.
This sounds like a similar problem I had where I wanted to use Views as email templates. The best answer I found for getting the string representation of a View was here: Render a view as a string
The best package I've found is the RazorPDF, available as a package at NuGet.org, based on iTextSharp. Works on Azure Web Sites:
https://nuget.org/packages/RazorPDF

Categories