ArgumentException: Parameter is not valid while copying from FileStream to MemoryStream - c#

I am trying to resize image using bitmap from Memorystream and save to directory. It works on the first run but if i try to update the image second time i am getting ArgumentException.
public IActionResult UpdatePhoto(int id, IFormFile file)
{
var company = _context.Companies.FirstOrDefault(x => x.Id == id);
var image = company.Logo;
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/companies", image);
if (System.IO.File.Exists(path))
{
System.IO.File.Delete(path);
}
ResizeImage(file, file.FileName);
company.Logo = file.FileName;
_context.Companies.Update(company);
_context.SaveChanges();
return RedirectToAction(nameof(Index));
}
I am getting error in Resize Method
public void ResizeImage(IFormFile file, string FileName)
{
using (var memoryStream = new MemoryStream())
{
file.CopyToAsync(memoryStream);
Bitmap original = (Bitmap)Image.FromStream(memoryStream);
Bitmap processed = new Bitmap(original,new Size(300,300));
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/companies", FileName );
processed.Save(path);
}

you shouldn't be using any of the async methods inside the methods which are not awaitable. updating your code to following should fix the issue.
public void ResizeImage(IFormFile file, string FileName)
{
using (var memoryStream = new MemoryStream())
{
file.CopyTo(memoryStream);
Bitmap original = (Bitmap)Image.FromStream(memoryStream);
Bitmap processed = new Bitmap(original,new Size(300,300));
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/companies", FileName );
processed.Save(path);
}
}

Related

Download multiple files .NET CORE Web API as ArchiveZip return root directory

I make a private class to get the bytes of the file and then return it into a zip file. Basically, I just want to pick specific files inside my folder and then zip it then download it. Here's my class:
private FileResult DownloadMultipleFiles(List<byte[]> byteArrayList)
{
var zipName = $"archive-EvidenceFiles-{DateTime.Now.ToString("yyyy_MM_dd-HH_mm_ss")}.zip";
using (MemoryStream ms = new MemoryStream())
{
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
foreach (var file in byteArrayList)
{
string fPath = Encoding.ASCII.GetString(file);
var entry = archive.CreateEntry(fPath, CompressionLevel.Fastest);
using (var zipStream = entry.Open())
{
zipStream.Write(file, 0, file.Length);
}
}
}
return File(ms.ToArray(), "application/zip", zipName);
}
}
And then, here's my controller:
[HttpGet("GetBundleFiles/{rhaId}")]
public async Task<IActionResult> GetBundleFiles(string rhaId)
{
List<byte[]> filesPath = new List<byte[]>();
var results = await _rhaFileEvidence.GetByRhaID(rhaId);
var files = results.ToList();
if (files.Count == 0)
return Ok(new { status = "null", message = "Empty data" });
files.ForEach(file =>
{
var fPath = file.FilePath;
byte[] bytes = Encoding.ASCII.GetBytes(fPath);
filesPath.Add(bytes);
});
return DownloadMultipleFiles(filesPath);
}
The controller works well, I can download the zip but when I open it, I can't get the files instead I get the root directory of the project I saved, like D:. I think I make mistake when making the memory stream or something, is there any suggestion how can I fix this? (Paste some solution code in the answer please)
you are getting the directory root because when you use archive.CreateEntry you are passing the file full path in parameter, you should be using only the file name
var entry = archive.CreateEntry(System.IO.Path.GetFileName(fPath), CompressionLevel.Fastest);
a second issue is that you actually saving the file path to your files not the content of the original file. you can update your DownloadMultipleFiles like this
private FileResult DownloadMultipleFiles(List<byte[]> byteArrayList)
{
var zipName = $"archive-EvidenceFiles-{DateTime.Now.ToString("yyyy_MM_dd-HH_mm_ss")}.zip";
using (MemoryStream ms = new MemoryStream())
{
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
foreach (var file in byteArrayList)
{
string fPath = Encoding.ASCII.GetString(file);
var entry = archive.CreateEntry(System.IO.Path.GetFileName(fPath), CompressionLevel.Fastest);
using (var zipStream = entry.Open())
{
var bytes = System.IO.File.ReadAllBytes(fPath);
zipStream.Write(bytes, 0, bytes.Length);
}
}
}
return File(ms.ToArray(), "application/zip", zipName);
}
}

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);
}
}

Unable to delete created and streamed file

I'm using Magick.NET to convert a PDF to a PNG and stream it back to the page via ajax.
Everything works until a PDF is uploaded twice. When trying to overwrite or delete the existing file, the debugger tells me that the file is in use by another process.
Here's my function that returns an Image to the controller:
//path is a fully qualified path to a file ending in .PDF
private Image ConvertPDFTOneImage(string path)
{
MagickReadSettings settings = new MagickReadSettings();
settings.Density = new PointD(300, 300);
using (MagickImageCollection images = new MagickImageCollection())
{
FileInfo file = new FileInfo(path);
images.Read(file);
file = null;
using (MagickImage horizontal = images.AppendHorizontally())
{
string PNGName = Path.ChangeExtension(path, ".png");
horizontal.Write(PNGName);
}
return Image.FromFile(path.Replace("pdf", "png"));
}
}
And my controller that streams the response back to the browser:
public async Task<HttpResponseMessage> PostFormData([FromUri] int sellerID, [FromUri] int eventID, [FromUri] string section, [FromUri] string row, [FromUri] string seat)
{
if (HttpContext.Current.Request.Files.AllKeys.Any())
{
try
{
string base64 = string.Empty;
SellerObjects.Externeal.SellerTicket TicketToSave = new SellerObjects.Externeal.SellerTicket();
TicketToSave.UploadedFile = HttpContext.Current.Request.Files["UploadedImage"];
SellerTicketRepo TheLocalSellerRepo = new SellerTicketRepo(TicketToSave);
using (MemoryStream ms = new MemoryStream())
{
TheLocalSellerRepo.GetConvertedPDFImage().Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
base64 = System.Convert.ToBase64String(ms.ToArray());
}
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StringContent(base64);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
return result;
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, "Error saving file.");
}
}
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "An error has occurred");
}
I am assuming that the Image class that you are returning is a System.Drawing.Image. You need to Dispose this object to release the file lock.
// Instead of this:
using (MemoryStream ms = new MemoryStream())
{
TheLocalSellerRepo.GetConvertedPDFImage().Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
base64 = System.Convert.ToBase64String(ms.ToArray());
}
// Should you be doing this:
using (MemoryStream ms = new MemoryStream())
{
using (Image img = TheLocalSellerRepo.GetConvertedPDFImage())
{
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
base64 = System.Convert.ToBase64String(ms.ToArray());
}
}
// Or you could even do this (if GetConvertedPDFImage() returns a MagickImage):
using (MagickImage img = TheLocalSellerRepo.GetConvertedPDFImage())
{
img.Format = MagickFormat.Jpeg;
base64 = img.ToBase64();
}

Mvvmcross PictureChooser Plugin - Droid - Picture orientation is wrong

I am using the following code to choose a photo on both iOS and Droid; however, on Droid, images taken in portrait are saved in landscape orientation. On iOS, the image saves with the correct orientation.
Mvx.Resolve<IMvxPictureChooserTask>().TakePicture(2000, 64, CaptureImageStream, () =>
{
/* don't do anything on cancel */
});
protected virtual void CaptureImageStream(Stream stream)
{
var fileStore = Mvx.Resolve<IMvxFileStore>();
const string folderName = "Observation_Photos";
fileStore.EnsureFolderExists(folderName);
//get file name
var fileName = RandomString(10);
while (fileStore.Exists(string.Format("{0}/{1}.jpg", folderName, fileName)))
{
fileName = RandomString(10);
}
//get file bytes
var fileContents = GetBytes(stream);
//write file
var fullPath = string.Format("{0}/{1}.jpg", folderName, fileName);
fileStore.WriteFile(fullPath, fileContents);
}
private static IEnumerable<byte> GetBytes(Stream stream)
{
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
Any ideas why the photo has the wrong orientation on droid or how to resolve the problem?
Update version of the MvxPictureChooserTask.cs from this pull request, https://github.com/MvvmCross/MvvmCross/pull/627, fixed the problem.

converting a base 64 string to an image and saving it

Here is my code:
protected void SaveMyImage_Click(object sender, EventArgs e)
{
string imageUrl = Hidden1.Value;
string saveLocation = Server.MapPath("~/PictureUploads/whatever2.png") ;
HttpWebRequest imageRequest = (HttpWebRequest)WebRequest.Create(imageUrl);
WebResponse imageResponse = imageRequest.GetResponse();
Stream responseStream = imageResponse.GetResponseStream();
using (BinaryReader br = new BinaryReader(responseStream))
{
imageBytes = br.ReadBytes(500000);
br.Close();
}
responseStream.Close();
imageResponse.Close();
FileStream fs = new FileStream(saveLocation, FileMode.Create);
BinaryWriter bw = new BinaryWriter(fs);
try
{
bw.Write(imageBytes);
}
finally
{
fs.Close();
bw.Close();
}
}
}
The top imageUrl declartion is taking in a Base64 image string, and I want to convert it into an image. I think my set of code only works for images like "www.mysite.com/test.jpg" not for a Base64 string. Anybody have some suggestions? Thanks!
Here is an example, you can modify the method to accept a string parameter. Then just save the image object with image.Save(...).
public Image LoadImage()
{
//data:image/gif;base64,
//this image is a single pixel (black)
byte[] bytes = Convert.FromBase64String("R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==");
Image image;
using (MemoryStream ms = new MemoryStream(bytes))
{
image = Image.FromStream(ms);
}
return image;
}
It is possible to get an exception A generic error occurred in GDI+. when the bytes represent a bitmap. If this is happening save the image before disposing the memory stream (while still inside the using statement).
You can save Base64 directly into file:
string filePath = "MyImage.jpg";
File.WriteAllBytes(filePath, Convert.FromBase64String(base64imageString));
Here is what I ended up going with.
private void SaveByteArrayAsImage(string fullOutputPath, string base64String)
{
byte[] bytes = Convert.FromBase64String(base64String);
Image image;
using (MemoryStream ms = new MemoryStream(bytes))
{
image = Image.FromStream(ms);
}
image.Save(fullOutputPath, System.Drawing.Imaging.ImageFormat.Png);
}
I would suggest via Bitmap:
public void SaveImage(string base64)
{
using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(base64)))
{
using (Bitmap bm2 = new Bitmap(ms))
{
bm2.Save("SavingPath" + "ImageName.jpg");
}
}
}
Here is working code for converting an image from a base64 string to an Image object and storing it in a folder with unique file name:
public void SaveImage()
{
string strm = "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
//this is a simple white background image
var myfilename= string.Format(#"{0}", Guid.NewGuid());
//Generate unique filename
string filepath= "~/UserImages/" + myfilename+ ".jpeg";
var bytess = Convert.FromBase64String(strm);
using (var imageFile = new FileStream(filepath, FileMode.Create))
{
imageFile.Write(bytess, 0, bytess.Length);
imageFile.Flush();
}
}
In my case it works only with two line of code. Test the below C# code:
String dirPath = "C:\myfolder\";
String imgName = "my_mage_name.bmp";
byte[] imgByteArray = Convert.FromBase64String("your_base64_string");
File.WriteAllBytes(dirPath + imgName, imgByteArray);
That's it. Kindly up vote if you really find this solution works for you. Thanks in advance.
In a similar scenario what worked for me was the following:
byte[] bytes = Convert.FromBase64String(Base64String);
ImageTagId.ImageUrl = "data:image/jpeg;base64," + Convert.ToBase64String(bytes);
ImageTagId is the ID of the ASP image tag.
If you have a string of binary data which is Base64 encoded, you should be able to do the following:
byte[] encodedDataAsBytes = System.Convert.FromBase64String(encodedData);
You should be able to write the resulting array to a file.
public bool SaveBase64(string Dir, string FileName, string FileType, string Base64ImageString)
{
try
{
string folder = System.Web.HttpContext.Current.Server.MapPath("~/") + Dir;
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
string filePath = folder + "/" + FileName + "." + FileType;
File.WriteAllBytes(filePath, Convert.FromBase64String(Base64ImageString));
return true;
}
catch
{
return false;
}
}
Using MemoryStream is not a good idea and violates a specification in MSDN for Image.FromStream(), where it says
You must keep the stream open for the lifetime of the Image.
A better solution is using ImageConverter, e.g:
public Image ConvertBase64ToImage(string base64)
=> (Bitmap)new ImageConverter().ConvertFrom(Convert.FromBase64String(base64));
In NetCore 6.0, you can use HttpClient and the async methods in the new File class.
The implementation is very simple:
static async Task DownloadFile(string imageUrl, string pathToSave)
{
var content = await GetUrlContent(url);
if (content != null)
{
await File.WriteAllBytesAsync(pathToSave, content);
}
}
static async Task<byte[]?> GetUrlContent(string url)
{
using (var client = new HttpClient())
using (var result = await client.GetAsync(url))
return result.IsSuccessStatusCode ? await result.Content.ReadAsByteArrayAsync():null;
}
Usage:
await DownloadFile("https://example.com/image.jpg", #"c:\temp\image.jpg");

Categories