Unable to delete created and streamed file - c#

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

Related

Blazor download html string as Image

public async Task ExportGraph(string projectName, string html)
{
var bytes = Encoding.Default.GetBytes(html);
using var stream = new MemoryStream(bytes);
stream.Seek(0, SeekOrigin.Begin);
using var streamRef = new DotNetStreamReference(stream: stream);
await JS.InvokeVoidAsync("downloadFileFromStream", $"FazerGraph-{projectName}.jpg", streamRef);
}
I have this method that receives a string containing the html I want to create an image from.
I-m getting this string from this JS method:
function getInnerHTMLElement(element) {
if (element) {
return element.innerHTML;
}
return null;
}
When I download the jpg image and try to open it says:
What I'm doing wrong?
I'm using now html converter like this:
public async Task ExportGraph(string projectName, string html)
{
var converter = new HtmlConverter();
var bytes = converter.FromHtmlString(html);
using var stream = new MemoryStream(bytes);
stream.Seek(0, SeekOrigin.Begin);
using var streamRef = new DotNetStreamReference(stream: stream);
await JS.InvokeVoidAsync("downloadFileFromStream", $"FazerGraph-{projectName}.jpg", streamRef);
}
and the result image I get is this:
What can I do to make it the same look like the html (shown bellow)?

Saving a Xamarin image to file

Hi so I'm trying to save an image selected by the user to file so that i can later upload it to my mySQL database.
So I have this code:
var result = await MediaPicker.PickPhotoAsync(new MediaPickerOptions
{
Title = "Please pick a selfie"
});
var stream = await result.OpenReadAsync();
resultImage.Source = ImageSource.FromStream(() => stream);
string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string filename = Path.Combine(path, "myfile");
using (var streamWriter = new StreamWriter(filename, true))
{
streamWriter.WriteLine(GetImageBytes(stream).ToString());
}
using (var streamReader = new StreamReader(filename))
{
string content = streamReader.ReadToEnd();
System.Diagnostics.Debug.WriteLine(content);
}
Here's the GetImageBytes(..) function:
private byte[] GetImageBytes(Stream stream)
{
byte[] ImageBytes;
using (var memoryStream = new System.IO.MemoryStream())
{
stream.CopyTo(memoryStream);
ImageBytes = memoryStream.ToArray();
}
return ImageBytes;
}
The code kind of works, it creates a file but doesn't save the image. Instead it saves "System.Bytes[]". It saves the name of the object, not the contents of the object.
myfile
enter image description here
Any help would be really appreciated. Thanks!
StreamWriter is for writing formatted strings, not binary data. Try this instead
File.WriteAllBytes(filename,GetImageBytes(stream));
Encode the byte array as a base64 string, then store that in the file:
private string GetImageBytesAsBase64String(Stream stream)
{
var imageBytes;
using (var memoryStream = new System.IO.MemoryStream())
{
stream.CopyTo(memoryStream);
imageBytes = memoryStream.ToArray();
}
return Convert.ToBase64String(imageBytes);
}
If you later need to retrieve the image bytes from the file, you can use the corresponding Convert.FromBase64String(imageBytesAsBase64String) method.

"Compressed folder is invalid" error returning a zip file from a Web API endpoint

I'm trying to compress/zip a Json string to a file and return that file from my method.
When I try to open the resulting zip file I get a message about the "compressed folder is invalid"
public FileContentResult GenerateZip()
{
var jsonResult = JsonSerializer.Serialize(GenerateReport());
FileContentResult outputFile;
using (var uncompressedStream = new MemoryStream(Encoding.UTF8.GetBytes(jsonResult)))
{
using (var compressedStream = new MemoryStream())
{
using (var compressorStream = new DeflateStream(compressedStream, CompressionLevel.Fastest, true))
{
uncompressedStream.CopyTo(compressorStream);
}
outputFile = new FileContentResult(compressedStream.ToArray(), "application/zip")
{
FileDownloadName = "TestReport.Zip"
};
}
}
return outputFile;
}
The above method is called from a WebAPI endpoint.
[HttpGet]
public FileContentResult GetZip()
{
var file = new ReportsBLL(_context, _logger, _config).GenerateZip();
return File(file.FileContents, file.ContentType, file.FileDownloadName);
}
I've also tried the above code using GZipStream instead of DeflateStream.

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

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

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