Uploaded Excel Blob not getting created properly - c#

The Excel file that I'm trying to upload to blob is not getting created properly although it is getting uploaded successfully. I'm using the UploadBlobAsync for the same.
private async Task UploadExcelToBlobAsync(string fileName, MemoryStream excelContentMemoryStream)
{
try
{
string currentDateTime = DateTime.Now.ToString("dd-MM-yyyy_HH-mm-ss");
string fileExtension = ".xslx";
fileName = $"{fileName}_{currentDateTime}{fileExtension}";
var blobContainerClient = _blobService.GetBlobContainerClient(); // this is properly instatiated
await blobContainerClient.UploadBlobAsync(fileName, excelContentMemoryStream);
}
catch (Exception ex)
{
throw ex;
}
}
Strangely though, the same MemoryStream object I'm using further in my code to perform some logic. So I guess, the issue is not with the MemoryStream object also. Below is a snip of the generated Excel, the generated file size is 14KB while the one that I had uploaded was of 20KB:
Can someone please tell me what I'm possibly doing wrong here? Many thanks in advance.
Addition: Below is the code which calls the method UploadExcelToBlobAsync:
public async Task<(string, List<CustomObject>?)> UploadExcelAsync(IFormFile file)
{
try
{
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
using (MemoryStream ms = new MemoryStream())
{
await file.CopyToAsync(ms);
var excelReader = ExcelReaderFactory.CreateOpenXmlReader(ms);
DataSet excelDataSet = excelReader.AsDataSet();
await UploadExcelToBlobAsync(file.FileName, ms);
var resp = await ValidateAndInsertExcelData(excelDataSet);
return resp;
}
}
catch(Exception ex)
{
throw ex;
}
}

Please try my code, if the downloaded file does not appear that prompt, my suggestion is to use my sample code.
[HttpPost]
[Route("UploadExcel")]
public async Task<IActionResult> UploadExcelAsync([FromForm] IFormCollection formfile)
{
string sasuri = "https://br***nWTT5GnC1LpHsD7p5Fo2lHpFM1nafAHjL9Fozf%2FvDdg%3D";
Uri container_uri = new Uri(sasuri);
var container_client = new BlobContainerClient(container_uri);
var file = formfile.Files.FirstOrDefault();
string fileName = file.FileName;
BlobClient blobClient = container_client.GetBlobClient(fileName);
Stream stream = file.OpenReadStream();
var result = await blobClient.UploadAsync(stream, true);
stream.Close();
return Ok(result.GetRawResponse().Status);
}
If you use my sample code, there is still such a prompt:
The file could be corrupted or unsafe. Unless you trust its source, don't open it.
Then please try to use another computer as client for test. If the issue still exists. It means the Microsoft office version or scurity has some problem. Then try this.

Related

Download a file using a ASP.NET Core Console Application From API Endpoint

I have an API which generates Word documents and serves them up on request when an endpoint is used, eg https://myapi.com/GetDocumentById/{id}
I'm trying to get my console application to access the URL and save the file to the local drive, eg C:\Temp
All solutions I've come accross so far have been centred around MVC which I'm obviously not using.
Could someone point me in the right direction please? What should I be using? I can't use WebClient as DownloadFile expects a filename and I won't necessarily know it. I need to download the generated file from the endpoint.
Thanks
public async Task<string> DownloadFile(string guid)
{
var fileInfo = new FileInfo($"{guid}.txt");
var response = await _httpClient.GetAsync($"{_url}/api/files?guid={guid}");
response.EnsureSuccessStatusCode();
await using var ms = await response.Content.ReadAsStreamAsync();
await using var fs = File.Create(fileInfo.FullName);
ms.Seek(0, SeekOrigin.Begin);
ms.CopyTo(fs);
return fileInfo.FullName;
}
You can use HttpClient to receive the file which returns from api, I have created a simple demo for your reference.
First, to create word file, you need to download DocumentFormat.OpenXml dll in your api project.
Api:
[Route("api/[controller]")]
[ApiController]
public class GetDocumentByIdController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GenerateDocx(int id)
{
using (MemoryStream mem = new MemoryStream())
{
using (WordprocessingDocument wordDocument =
WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document))
{
// Add a main document part.
MainDocumentPart mainPart = wordDocument.AddMainDocumentPart();
// Create the document structure and add some text.
mainPart.Document = new Document();
Body body = mainPart.Document.AppendChild(new Body());
Paragraph para = body.AppendChild(new Paragraph());
Run run = para.AppendChild(new Run());
run.AppendChild(new Text("The text in docx which created by " + id.ToString()));
}
return File(mem.ToArray(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
}
}
}
Console app:
class Program
{
static void Main(string[] args)
{
try
{
UriBuilder builder = new UriBuilder("http://localhost:50855/api/ApiCode/GenerateDocx/1");
builder.Query = "id=1";// you can pass any other value
HttpClient client = new HttpClient();
var contentBytes = client.GetByteArrayAsync(builder.Uri).Result;
MemoryStream stream = new MemoryStream(contentBytes);
FileStream file = new FileStream(#"C:\Temp\ABC.docx", FileMode.Create, FileAccess.Write);
stream.WriteTo(file);
file.Close();
stream.Close();
}
catch (Exception)
{
throw; //throws 'TypeError: Failed to fetch'
}
}
}
After running the console app, you will find a word file named ABC in the path of C:\Temp.

How to download a png file with ASP.NET Core

I need the user to be able to download png images from my site. When the mthod runs it completes without errors but no image is downloaded. I do not need the user to see a pop-up dialog thought it is certainly helpful. This is what I have right now:
public async Task<IActionResult> DownloadImage(string filename)
{
var path = Path.GetFullPath("./wwwroot/images/school-assets/" + filename);
MemoryStream memory = new MemoryStream();
using (FileStream stream = new FileStream(path, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, "image/png", "download");
}
This method is called by an ajax call in the view that looks like this
$.ajax({
url: "./MyHome/DownloadImage",
type: "Get",
data: {filename : filename},
success: function (file) {
},
error: function (request, status, error) {
console.log(request.responseText);
}
});
}
Edit:
If i console.log file in the success portion i see a string of bytes so I know it is creating the file but not letting the user get to i. I have tried content disposition and creating a physical file result as suggested.
For File, you need to provide the file name with file extension, otherwise, the downloaded file will not be able to open.
Try something like
public async Task<IActionResult> DownloadImage(string filename)
{
var path = Path.GetFullPath("./wwwroot/images/school-assets/" + filename);
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));
}
You need to set the content dispositon type to enable direct downloading of the file :
public IActionResult OnGetPng()
{
var bytes = System.IO.File.ReadAllBytes("test.png");
var cd = new System.Net.Mime.ContentDisposition
{
FileName = "test.png",
Inline = false
};
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("X-Content-Type-Options", "nosniff");
return File(bytes, "image/png");
}
If you prefer you can also make use of the PhysicalFileResult type which takes care of your stream and return FileResult from your controller. In that case your code looks like this:
var fn = Path.Combine(env.WebRootPath, "test.png");
var contentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
Response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString();
return new PhysicalFileResult(fn, "image/jpeg");
To get access to the WebRootPath you have to inject IHostingEnvironment env into your constructor.
#Caleb sir from below code you can download png file.
Download png file from folder
[HttpGet]
public FileStreamResult DownloadPngFile(string fileName)
{
var stream = new FileStream(Directory.GetCurrentDirectory() + "\\wwwroot\\images\\school-assets\\" + fileName, FileMode.Open);
return new FileStreamResult(stream, "image/png");
}
Download png file from database
[HttpGet]
public FileStreamResult DownloadPngFileFromDataBase(string id)
{
var _fileUpload = _db.ImageFileUpload.SingleOrDefault(aa => aa.fileid == id);
// _fileUpload.FileContent column type is byte
MemoryStream ms = new MemoryStream(_fileUpload.FileContent);
return new FileStreamResult(ms, "image/png");
}
For more info please also see this question and answer. Download Pdf file in asp.net core (accepted answer) and one more extra link
Download files in asp.net core
This code can save photos from URL addresses in the server folder.
private readonly Lazy<HttpClient> _client;
In constructor:
_client = new Lazy<HttpClient>(() => clientFactory.CreateClient());
That is better to use lazy loading in a way the server will not spend additional resources to create HttpClient immediately.
public async Task<string> SavePhotoInFolder(string url)
{
string photoPath = $"/Photos/{Guid.NewGuid()}.jpeg";
using (var request = new HttpRequestMessage(HttpMethod.Get, url))
using (
Stream contentStream = await (await _client.Value.SendAsync(request)).Content.ReadAsStreamAsync(),
stream = new FileStream($"{_appEnvironment.WebRootPath}{photoPath}", FileMode.Create))
{
await contentStream.CopyToAsync(stream);
}
return photoPath;
}
You can use HttpClient
using (var client = new HttpClient())
{
try
{
using var result = await client.GetAsync($"http://{url}");
if (result.IsSuccessStatusCode)
{
return await result.Content.ReadAsByteArrayAsync();
}
}
catch(Exception ex)
{
Console.WriteLine(ex.InnerException);
}
}

Angular 7 unable to download file with c# .net core api

I have an angular application where one of the grids has column with hyperlink to a csv file. This csv is on a network drive.
When user click the link, they should be able to save file to their machine (assuming all users have access).
I am using file-saver to download/save the file.
This is my c# core api controller. For testing, I have hard coded file name c:\temp\test.csv. In real application, Angular will call the api with desired file (including full path).
[HttpGet("DownLoadFile/{fileName}")]
public HttpResponseMessage DownLoadFile( string fileName)
{
try
{
fileName = #"c:\temp\test.csv"; //Hard Coding for testing only.
if (!string.IsNullOrEmpty(fileName))
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
var fileStream = new FileStream(fileName, FileMode.Open);
response.Content = new StreamContent(fileStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = fileName;
return response;
//return ResponseMessage(response);
}
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
catch (Exception ex)
{
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
}
Angular Service:
public getFile(filePath:string): Observable<Blob>{
console.log(filePath);
let path = 'http://localhost:19090/api/testAPI/DownLoadFile/testfile.csv';
let options = new RequestOptions({responseType: ResponseContentType.Blob });
return this._http.get(path, {responseType: 'blob'})
.pipe(
catchError(this.handleError));
}
Component Code:
My application uses Kendo Grid.
private downloadFile( dataItem: any):void{
this._sharedService.getFile(dataItem.test_file)
.subscribe(
(data:Blob) => {
saveAs(data, `Test_1.csv`); // from file-saver library
},
(err: any) => {
console.log(`Unable to save file ${JSON.stringify(err)}`)}
);
}
This code does download a csv file but it appears as :
{"version":{"major":1,"minor":1,"build":-1,"revision":-1,"majorRevision":-1,"minorRevision":-1},"content":{"headers":[{"key":"Content-Type","value":["text/csv"]},{"key":"Content-Disposition","value":["attachment; filename=\"c:\\temp\\blaze.csv\""]}]},"statusCode":200,"reasonPhrase":"OK","headers":[],"requestMessage":null,"isSuccessStatusCode":true}
What am I doing wrong? Any pointers?
These are some of the links I used to troubleshoot:
How do I download a file with Angular2
Angular download csv file with Web Api 2
UPDATE:
Thanks to this link - https://expertcodeblog.wordpress.com/2018/08/13/asp-net-core-angular-how-to-download-file-from-api-controller/
I was able to solve my problem. I updated my Controller code and I am able to download the file.
As per my update, I was able to update my controller and that solved my issue. No change on Client code.
public async Task<IActionResult> DownloadFile(string filename)
{
try
{
string file = #"c:\temp\test.csv";
var memory = new MemoryStream();
using (var stream = new FileStream(file, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, GetMimeType(file), filename);
}
catch (Exception e)
{
return BadRequest(e);
}
}

System.OutOfMemoryException when reading content of a file in a Web API

I want to send the content of file as memory stream to S3 bucket via Amazon Firehose. below is my attempt which works fine for small files, but I have a file of 1 GB and I am getting {"Exception of type 'System.OutOfMemoryException' was thrown."}.
My code snippet:
[HttpPost]
public async Task<bool> Upload()
{
try
{
var filesReadToProvider = await Request.Content.ReadAsMultipartAsync();
foreach (var stream in filesReadToProvider.Contents)
{
var fileBytes = await stream.ReadAsByteArrayAsync(); // THIS IS WHERE EXCEPTION COMES
using (MemoryStream memoryStream = new MemoryStream(fileBytes))
{
PutRecordRequest putRecord = new PutRecordRequest();
putRecord.DeliveryStreamName = myStreamName;
Record record = new Record();
record.Data = memoryStream;
putRecord.Record = record;
await kinesisClient.PutRecordAsync(putRecord);
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
return true;
}
I looked into this link OutOfMemoryExceptoin but I could not comprehend it. Please help me.
Attempt 1:
var filesReadToProvider = await Request.Content.ReadAsMultipartAsync();
foreach (var stream in filesReadToProvider.Contents)
{
var fileByte = await stream.ReadAsStreamAsync();
MemoryStream _ms = new MemoryStream();
fileByte.CopyTo(_ms); // EXCEPTION HERE
try
{
PutRecordRequest putRecord = new PutRecordRequest();
putRecord.DeliveryStreamName = myStreamName;
Record record = new Record();
record.Data = _ms;
putRecord.Record = record;
await kinesisClient.PutRecordAsync(putRecord);
}
catch (Exception ex)
{
Console.WriteLine("Failed to send record to Kinesis. Exception: {0}", ex.Message);
}
}
[HttpPost]
public async Task<bool> Upload()
{
try
{
using(var requestStream = await Request.Content.ReadAsStreamAsync())
{
PutRecordRequest putRecord = new PutRecordRequest();
putRecord.DeliveryStreamName = myStreamName;
Record record = new Record();
record.Data = requestStream ;
putRecord.Record = record;
await kinesisClient.PutRecordAsync(putRecord);
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
return true;
}
This will read the data in chunks. Keep everything in the Stream so you don't keep all the bytes around in a huge array.
When reading large files, I use StreamReader's Readline() method. It works on large files as it manages file system caching internally. Can you use this method, instead? Is there a reason why you are implementing the MemoryStream class? You have a comment asking how to inject the data? Did you try using one of MemoryStream's methods???
https://learn.microsoft.com/en-us/dotnet/api/system.io.memorystream?view=netframework-4.7.2
Update:
Not sure if this is helpful since the code is substantially different from what you are using. But, yours isn't working either, so just a suggestion.
http://www.tugberkugurlu.com/archive/efficiently-streaming-large-http-responses-with-httpclient

How to upload image on amzon s3 using .net web api c#?

I have create one api for the image upload. in this code i have upload time image download in my local folder and store. but i need now change my code and move this image download on amzon s3. i have found one link in searching time but in this link static image is upload i need image browse from the file upload control and download on amzon server. but how can do that i have no idea. please any one how can do that then please help me. here below listed my code. and also add i have try this code in below.
this is my api method for the image upload :
[HttpPost]
[Route("FileUpload")]
public HttpResponseMessage FileUpload(string FileUploadType)
{
try
{
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
string fname = System.IO.Path.GetFileNameWithoutExtension(postedFile.FileName.ToString());
string extension = Path.GetExtension(postedFile.FileName);
Image img = null;
string newFileName = "";
newFileName = DateTime.Now.ToString("yyyyMMddhhmmssfff") + ".jpeg";
string path = ConfigurationManager.AppSettings["ImageUploadPath"].ToString();
string filePath = Path.Combine(path, newFileName);
SaveJpg(img, filePath);
return Request.CreateResponse(HttpStatusCode.OK, "Ok");
}
}
}
catch (Exception ex)
{
return ex;
}
return Request.CreateResponse(HttpStatusCode.OK, "Ok");
}
This is my save image api =>
public static void SaveJpg(Image image, string file_name, long compression = 60)
{
try
{
EncoderParameters encoder_params = new EncoderParameters(1);
encoder_params.Param[0] = new EncoderParameter(
System.Drawing.Imaging.Encoder.Quality, compression);
ImageCodecInfo image_codec_info =
GetEncoderInfo("image/jpeg");
image.Save(file_name, image_codec_info, encoder_params);
}
catch (Exception ex)
{
}
}
i have try this code with static image upload on server =>
private string bucketName = "Xyz";
private string keyName = "abc.jpeg";
private string filePath = "C:\\Users\\I BALL\\Desktop\\image\\abc.jpeg";. // this image is store on server
public void UploadFile()
{
var client = new AmazonS3Client(Amazon.RegionEndpoint.USEast1);
try
{
PutObjectRequest putRequest = new PutObjectRequest
{
BucketName = bucketName,
Key = keyName,
FilePath = filePath,
ContentType = "text/plain"
};
PutObjectResponse response = client.PutObject(putRequest);
}
catch (AmazonS3Exception amazonS3Exception)
{
if (amazonS3Exception.ErrorCode != null &&
(amazonS3Exception.ErrorCode.Equals("InvalidAccessKeyId")
||
amazonS3Exception.ErrorCode.Equals("InvalidSecurity")))
{
throw new Exception("Check the provided AWS Credentials.");
}
else
{
throw new Exception("Error occurred: " + amazonS3Exception.Message);
}
}
}
here i have show my code but i need to marge with my code so how can do that please any one know how can do that.
This might be too late, but here is how I did it:
Short Answer: Amazon S3 SDK for .Net has a class called "TransferUtility" which accepts a Stream object, so as long as you can convert your file to any Class derived from the abstract Stream class, you can upload the file.
Long Answer:
The httprequest posted files has an inputStream property, so inside your foreach loop:
var postedFile = httpRequest.Files[file];
If you expand on this object, it is of type "HttpPostedFile", so you have access to the Stream through the InputStream property:
Here is some snippets from a working sample:
//get values from the headers
HttpPostedFile postedFile = httpRequest.Files["File"];
//convert the posted file stream a to memory stream
System.IO.MemoryStream target = new System.IO.MemoryStream();
postedFile.InputStream.CopyTo(target);
//the following static function is a function I built which accepts the amazon file key and also the object that will be uploaded to S3, in this case, a MemoryStream object
s3.WritingAnObject(fileKey, target);
The S3 is an instance of a class called "S3Uploader", here are some snippets that can get you going,
below are some needed namespaces:
using Amazon;
using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Transfer;
class constructor:
static IAmazonS3 client;
static TransferUtility fileTransferUtility;
public S3Uploader(string accessKeyId, string secretAccessKey,string bucketName)
{
_bucketName = bucketName;
var credentials = new BasicAWSCredentials(accessKeyId, secretAccessKey);
client = new AmazonS3Client(credentials, RegionEndpoint.USEast1);
fileTransferUtility = new TransferUtility(client);
}
Notice here that we are creating the credentials using the BasicAWSCredentials class instead of passing it to the AmazonS3Client directly. And then we are using fileTransferUtility class to have better control over what is sent to S3. and here is how the Upload works based on Memory Stream:
public void WritingAnObject(string keyName, MemoryStream fileToUpload)
{
try
{
TransferUtilityUploadRequest fileTransferUtilityRequest = new
TransferUtilityUploadRequest
{
StorageClass = S3StorageClass.ReducedRedundancy,
CannedACL = S3CannedACL.Private
};
fileTransferUtility.Upload(fileToUpload, _bucketName, keyName);
}
catch (AmazonS3Exception amazonS3Exception)
{
//your error handling here
}
}
Hope this helps someone with similar issues.

Categories