Downloading a document in PWA app ASP.NET MVC - c#

Just curious about this and wanted to know about this matter.
I have downloaded a PWA app and set it to my ASP.NET MVC project.
In there I want to allow download a file and for that I have used this,
<a download href="#Url.Action("DownloadDoc","Ajax" ,new { id = item.Id})" target="_blank" class="btn-20 btn border-green-dark color-green-dark bi bi-arrow-down" />
The issue is it's not downloading, A new view will appear and the address bar shows the controller and the action, and the file Id, If I inspect this, in the network area I can see the file.
Normally when I used the same in HTML view, it opens up a new tab and then closes with downloading the file.
Here it won't work.
I want to know is there is another way of doing this ??
This is my controller code
public ActionResult DownloadDoc(int id) {
var q = from temp in db.CustomerScannedDocuments where temp.Id == id select temp.Attachment;
var type = from t in db.CustomerScannedDocuments where t.Id == id select t.File_Name;
string fileType = type.First().ToString();
string ext = Path.GetExtension(fileType);
byte[] cover = q.First();
if (cover != null) {
if (ext == ".pdf") {
return File(cover, "application/pdf");
} else {
return File(cover, "image/jpg");
}
} else {
return null;
}
}

I want to know is there is another way of doing this ?
There are couple of ways you could achieve your requirement. You could try in following ways:
Way: 1
Controller:
public async Task<IActionResult> DownloadImage(string imageName)
{
var path = Path.GetFullPath("./wwwroot/ImageName/Cover/" + imageName);
MemoryStream memory = new MemoryStream();
using (FileStream stream = new FileStream(path, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, "image/png", Path.GetFileName(path));
}
Download Link HTML:
<a asp-controller="Application" asp-action="DownloadImage" class="btn btn-info" asp-route-imageName="#item.ImageName">Download</a>
Output:
Note: It doesn't require any Nuget package
Way: 2 When you want to display PDF on your browser from database Model
Solution:
public ActionResult DisplayPdfOnBrowserFromDatabaseList()
{
var data = _context.Members.ToList();
var pdf = data.ToPdf();
MemoryStream ms = new MemoryStream(pdf);
return new FileStreamResult(ms, "application/pdf");
}
Note: To handle this scenario you need to use 3rd party Nuget package which is ArrayToPdf You can search on Nuget package on your visual studio. You can also check the GitHub Link. Finally need to add using ArrayToPdf; on top.
Output:
Way: 3 When you want to download PDF on your browser from database model or existing file
Solution:
public ActionResult DownloadPDFOnBrowser()
{
var data = _context.Members.ToList();
var byteArray = data.ToPdf();
MemoryStream stream = new MemoryStream(byteArray);
string mimeType = "application/pdf";
return new FileStreamResult(stream, mimeType)
{
FileDownloadName = "DatabaseListToPdf.pdf"
};
}
Note: As described above we need to add using ArrayToPdf; on top. To handle this scenario you need to use 3rd party Nuget package which is ArrayToPdf You can search on Nuget package on your visual studio. See the screenshot below:

Related

How do I render a pdf in the browser generated from DynamicPDF Cloud API?

Using DynamicPDF's Cloud API, instead of generating a pdf back to the local file system, I would like it to directly open in another browser tab to be available for printing immediately. How do I accomplish that?
The method I am using (.NET Core 6 / Blazor) is below:
public async Task CallDynPDFCloudAPI()
{
var basePath = #"JSONFiles\";
var apiKey = "foo";
var cloudPath = "bar.dlex";
Pdf pdf = new Pdf();
pdf.ApiKey = apiKey;
LayoutDataResource layoutDataResource = new LayoutDataResource(basePath + "FooBar.json");
pdf.AddDlex(cloudPath, layoutDataResource);
PdfResponse pdfResponse = pdf.Process();
if (pdfResponse.IsSuccessful)
{
File.WriteAllBytes(basePath + "Manifest_" + manifestBranch + ".pdf", pdfResponse.Content);
}
else
{
Console.WriteLine(pdfResponse.ErrorJson);
}
}
Reread article on https://learn.microsoft.com/en-us/aspnet/core/blazor/file-downloads?view=aspnetcore-6.0
#page "/file-download-1"
#using System.IO
#inject IJSRuntime JS
<h1> File Download Example</h1>
<button #onclick = "DownloadFileFromStream" >
Download File From Stream
</button>
#code {
private Stream CallDynPDFCloudAPI()
{
var basePath = #"JSONFiles\";
var apiKey = "foo";
var cloudPath = "bar.dlex";
Pdf pdf = new Pdf();
pdf.ApiKey = apiKey;
LayoutDataResource layoutDataResource = new LayoutDataResource(basePath + "FooBar.json");
pdf.AddDlex(cloudPath, layoutDataResource);
PdfResponse pdfResponse = pdf.Process();
if (pdfResponse.IsSuccessful)
{
return new MemoryStream(pdfResponse.Content);
}
else
{
throw new Exception("");
}
}
private async Task DownloadFileFromStream()
{
var fileStream = CallDynPDFCloudAPI();
var fileName = "file.pdf";
using var streamRef = new DotNetStreamReference(stream: fileStream);
await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
}
You won't be able to access the PDF content for this request from another browser tab. I'd recommend opening the new tab before making the call and then streaming it there. If you're using an 'a href' link, you can accomplish this by setting the 'target="_blank"' property of the 'a href'. If this is a form submission, you can set the 'target="_blank"' property of the 'form'.
The other option would be to store the PDF somewhere temporarily (as a file, in a DB or in BLOB storage) then stream it to the other tab once it's opened.
Ive impletented #Mihal's answer, with modified code suggested by #DynamicPDF to achieve the result I was looking for. My two goals were:
Not clutter the client device with excessive dowloaded files
Not needing to save the file in Server or DB
Javascript:
<script>
window.downloadFileFromStream = async (fileName,
contentStreamReference) => {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
//--Opens PDF file in new Tab
fetch(url)
.then(response => response.blob())
.then(data => window.open(URL.createObjectURL(data), '_blank'))
//--Downloads file to Browser (uncomment if desired)
//const anchorElement = document.createElement('a');
//anchorElement.href = url;
//anchorElement.download = fileName ?? 'Manifest';
//anchorElement.click();
//anchorElement.remove();
//URL.revokeObjectURL(url);
}
</script>
*NOTE! My application is purely internal-facing to our organization. Our Windows client machines and browsers are managed by Group Policy. I have not yet tested this on Mac / Safari clients yet.

Send file from Android Kotlin to C# using POST

We have two applications: A C# REST-API, and a Kotlin Android application, we are using Google Platform Cloud Bucket to host the images.
A picture will be uploaded on the Android application, but the C# REST-API needs to upload it to the Google Cloud Platform.
This is the working C# code to upload a file to the Google Cloud Buckets:
[HttpPost]
[Route("upload")]
public IActionResult Upload()
{
var storageClient = StorageClient.Create(google_credentials);
string fileToUpload ="/Users/niel/Downloads/new_cat.jpg";
using (var fileStream = new FileStream(fileToUpload, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
storageClient.UploadObject("test_storage_fotos", "new_cat", "image/jpeg", fileStream);
}
Console.WriteLine("uploaded the file successfully");
return Ok();
}
Now I need to replace fileToUpload with the content from a POST-request. Is there a way to do this? Picture from Android app > C# API > Google Buckets? The link from the C# API to Google Buckets is already working.
Is there a way in Kotlin to somehow get the byte-string of an image, post it to my C# API who takes the content and puts it in a FileStream? I than can upload the FileStream using storageClient.UploadObject? Is this a possibility?
Thanks!
Yes, you can definitely do this. Just send the file over to the server via http protocol with multipart/form-data content type.
In kotlin you can use ktor or any other http library to do that.
For ktor you'll need to add an implementation dependency
implementation "io.ktor:ktor-client-android:1.5.4"
And you might also need to add additional permission in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
Then you can send a file with this snippet. Notice that imageUri is a content uri, for file uri the code would be a bit different
private fun getFileName(resolver: ContentResolver, uri: Uri): String {
val returnCursor: Cursor = resolver.query(uri, null, null, null, null)!!
val nameIndex: Int = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
returnCursor.moveToFirst()
val name: String = returnCursor.getString(nameIndex)
returnCursor.close()
return name
}
suspend fun postAndImage(imageUri: Uri, uploadEndPoint: String) {
val client = HttpClient(Android)
val cr = applicationContext.contentResolver
if(cr.getType(imageUri) == null) {
//process error
return
}
val stream = cr.openInputStream(imageUri)
if(stream == null) {
//process error
return
}
val response: HttpResponse = client.submitFormWithBinaryData(
url = uploadEndPoint,
formData = formData {
append("image", InputProvider { stream.asInput() }, Headers.build {
append(HttpHeaders.ContentType, cr.getType(imageUri)!!)
append(HttpHeaders.ContentDisposition, "filename=${getFileName(cr, imageUri)}")
})
}
)
stream.close()
//process response
}
And you'll need to modify you upload function slightly
[HttpPost]
[Route("upload")]
//the name of the argument must match the key that you pass in "append" function
public async Task<IActionResult> Post(IFormFile image)
{
var storageClient = StorageClient.Create(google_credentials);
using (var stream = image.OpenReadStream())
{
//it's also possible to get original file name from file name property
var fileName = Guid.NewGuid() + "." + Path.GetExtension(image.FileName);
//assuming bucket is already created
var storageObject = await storageClient
.UploadObjectAsync("test_storage_fotos", fileName, "image/jpeg", stream);
//save information about a storage object in database
}
return Ok();
}

Angular 5 - uploads disappear after publishing new version

In an angular 5 app with a .net core backend, a user can upload attachments for his/her projects. The attachments are being send to the .NET Core backend and are saved inside the WebRootPath in a folder with the name of the project code.
[HttpPost]
[Route("api/projects/{id}/upload")]
[Consumes("application/json", "application/json-patch+json", "multipart/form-data")]
public async Task<IActionResult> UploadAttachment(int id, FileForUpload upload)
{
var project = await _orbaAgent.GetDefaultProject(id);
if (upload != null && upload.File != null && upload.File.Length > 0)
{
var file = upload.File;
string serverPath = string.IsNullOrEmpty(_hostingEnvironment.WebRootPath) ? _hostingEnvironment.ContentRootPath : _hostingEnvironment.WebRootPath;
serverPath = Path.Combine(serverPath, "attachments", project.Code);
if (!Directory.Exists(serverPath))
{
Directory.CreateDirectory(serverPath);
}
using (var fileStream = new FileStream(Path.Combine(serverPath, file.FileName), FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
}
return Ok();
}
The location of the attachments becomes 'app/attachments/'projectcode'/.
To download the files, I use an anchor element on the html page
<a href="attachments/{{defaultProject.code}}/{{att.name}}" download>{{att.name}}</a>
Uploading/downloading works perfect.
The only problem is that when I deploy a new version of the application, the app/attachment folder dissapears and so do all of the attachments.
Is there any way to prevent the deletion of this folder for new versions? Or if this is not possible, should I save the files somewhere else?

Show download progress in browser

I'm working in a function to download pdfs from DropBox, I'm using ASP.net Core , everything works good. The only thing is that when you click in the download link it doesn't show any message and downloads the file. I would like to show the download progress like usually happens when we download something from Internet, I don't want any dialog to appear, just to show that the file was downloaded like normally happens in any browser like Chrome or IE and then have the choices 'Show in Folder' and things like that, what would I need to add?
public async Task DownloadPdf()
{
DropboxClient client2 = new DropboxClient("cU5M-a4exaAAAAAAAAABDVZsKdpPteNmwHslOeFEo-HByuOr4v4ONvXoAMCFyOXH");
string folder = "MyFolder";
string file = "Test PDF.pdf";
using (var response = await client2.Files.DownloadAsync("/" + folder + "/" + file))
{
using (var fileStream = System.IO.File.Create(#"C:\Users\User\Downloads\Test.pdf"))
{
(await response.GetContentAsStreamAsync()).CopyTo(fileStream);
}
}
}
I have a asp.net core project with an API that returns a file:
[HttpGet("{id}")]
public IActionResult Get(int id) {
byte[] fileContent = READ_YOUR_FILE();
FileContentResult result = new FileContentResult(fileContent, "application/octet-stream") {
FileDownloadName = id.ToString()
};
return result;
}
If I access in my browser the URL from this API (myapp/api/mycontroller/id), then I can see the file downloading.

Open external PDF file in asp.net MVC 2

I know how to open an internal pdf file :
public ActionResult GetPDF( string filename )
{
return File( filename, "application/pdf", Server.HtmlEncode( filename ) );
}
question is, how to open a PDF file from an other/external website, e.g. http://example.com/mypdffile.pdf
You don't really need a controller action to do this. You could simply:
Open mypdffile.pdf
Of course if you want to hide this address from the user you could use a WebClient to fetch it on the server:
public ActionResult GetPDF()
{
using (var client = new WebClient())
{
var buffer = client.DownloadData("http://www.blabla.com/mypdffile.pdf");
return File(buffer, "application/pdf", "mypdffile.pdf");
}
}
And in your view:
<%= Html.ActionLink("Download PDF", "GetPDF") %>
You will need it locally anyway to do any processing so, you can download it to local folder and then show it. use WebClient or HttpRequest/HttpResponse objects to do the downloading

Categories

Resources