Convert from HttpPostedFileBase to image produce error due position - c#

I have the following controller, which upon receiveing a file from the view generates a thumbnail and saves both, image abd thumbnail to the database:
public async Task<ActionResult> FileUpload(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
var logo = new Logo() { LogoGuid = Guid.NewGuid() };
var clinica = await GetClinicaAsync();
if (IsClinicNonexistent(clinica))
{
db.Logos.Add(logo);
clinica.LogoGuid = logo.LogoGuid;
}
else
logo = await db.Logos.FirstOrDefaultAsync(l => l.LogoGuid == clinica.LogoGuid);
logo.LogoImage = GetImage(file);
logo.Thumbnail = GetThumbnail(file);
db.SaveChanges();
}
return RedirectToAction("Arquivos");
}
Which, for the matters of this error calls a method:
private byte[] GetThumbnail(HttpPostedFileBase file)
{
var image = Image.FromStream(file.InputStream);
var thumbnailSize = GetThumbnailSize(image);
var memoryStream = new MemoryStream();
var thumbnail = image.GetThumbnailImage(thumbnailSize.Width, thumbnailSize.Height, null, IntPtr.Zero);
image.Save(memoryStream, ImageFormat.Jpeg);
return memoryStream.ToArray();
}
That configuration of code produces an erro while running the line
var image = Image.FromStream(file.InputStream);
The error is: "System.ArgumentException: Parameter is not valid."
All documentation that I could refer to, makes me believe that it's all sintatically and logically correct, yet there is the error...
But, tne funny thing is, if a move the conversion line which produces error, do the calling method, specifically to the begining as:
public async Task<ActionResult> FileUpload(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
var logo = new Logo() { LogoGuid = Guid.NewGuid() };
var clinica = await ClinicaAsync();
var image = Image.FromStream(file.InputStream);
There will be no error. Please notice that the convertion line is in the last line of the code just above, thus in the begining of the controller. Again, if I move that line further down, but still in the controller, there will be the error again...
How can I keep the line where it logically belongs, thus in the GetThumbnail method?
Just as an additional reference, the GetImage method is:
private byte[] GetImage(HttpPostedFileBase file)
{
using (var br = new BinaryReader(file.InputStream))
return br.ReadBytes(file.ContentLength);
}

The first thing you should know is Image.FromStream() requires a stream (MemoryStream, InputStream, etc.) that hasn't been already read, or the position is reset to beginning using Seek() method.
First, try to reset the pointer before calling GetThumbnail() method by using Position property or Seek():
// alternative 1
logo.LogoImage = GetImage(file);
file.InputStream.Position = 0;
logo.Thumbnail = GetThumbnail(file);
// alternative 2
logo.LogoImage = GetImage(file);
file.InputStream.Seek(0, SeekOrigin.Begin);
logo.Thumbnail = GetThumbnail(file);
If this doesn't work, the possible cause is you're calling Image.FromStream() after HttpPostedFileBase.InputStream has already used (and disposed) by BinaryReader which calls Dispose() (the using statement automatically called it), thus the exception is thrown.
You can create a copy of MemoryStream from HttpPostedFileBase.InputStream using Stream.CopyTo() like below example:
public async Task<ActionResult> FileUpload(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
var logo = new Logo() { LogoGuid = Guid.NewGuid() };
var clinica = await GetClinicaAsync();
var ms = new MemoryStream();
file.InputStream.CopyTo(ms); // copy HTTP stream to memory stream
if (IsClinicNonexistent(clinica))
{
db.Logos.Add(logo);
clinica.LogoGuid = logo.LogoGuid;
}
logo.LogoImage = GetImage(file);
ms.Position = 0; // make sure the stream is read from beginning
logo.Thumbnail = GetThumbnail(ms);
db.SaveChanges();
}
return RedirectToAction("Arquivos");
}
Then GetThumbnail() parameter should be changed to accept MemoryStream copied from HTTP stream:
// get image from memory stream
private byte[] GetThumbnail(MemoryStream file)
{
var image = Image.FromStream(ms);
var thumbnailSize = GetThumbnailSize(image);
var thumbnail = image.GetThumbnailImage(thumbnailSize.Width, thumbnailSize.Height, null, IntPtr.Zero);
image.Save(file, ImageFormat.Jpeg);
return file.ToArray();
}
Note: You can also save the stream as image file on the disk and then read it with Server.MapPath() to generate thumbnail.
Related issues:
Image.FromStream(PostedFile.InputStream) Fails. (Parameter is not valid.) (AsyncFileUpload))
How do you convert a HttpPostedFileBase to an Image?

Related

How to download edited PDF using ITextSharp and C# Blazor

I have a Blazor application in which I'm trying to download a PDF I have edited using ITextSharp. All resources I search tend to use the Response class which I don't have (I believe because I am using .NET 6). I found some solutions that that send the byte array to a javascript function but I'm having no such luck. I get the error
System.ArgumentNullException: 'Value cannot be null.' When I Envoke the JS function.
I've posted my code below. Though I'm not passing any null objects, I do see some potential errors in my memory stream and I'm not sure why (See Image). Any information on where I'm going wrong or any easy approach would be appreciated. Thanks.
MemoryStream output = new MemoryStream();
PdfReader pdfReader = new PdfReader("MY_SOURCE_PATH_CHANGED_FOR_POST");
PdfStamper pdfStamper = new PdfStamper(pdfReader, output);
// Make Edits
//editPdf(ref pdfStamper.AcroFields);
pdfStamper.FormFlattening = false;
pdfStamper.Close();
await JSRuntime.InvokeVoidAsync("saveAsFile", "test.pdf", output.ToArray());
And the JS
function saveAsFile(filename, bytesBase64)
{
if (navigator.msSaveBlob) {
var data = window.atob(bytesBase64);
var bytes = new Unit8Array(data.length);
for (var i = 0; i < data.length; i++) {
bytes[i] = data.charCodeAt(i);
}
var blob = new Blob([bytes.buffer], { type: "application/octet-stream" });
navigator.msSaveBlob(blob, filename);
}
else
{
var link = document.createElement("a");
link.download = filename;
link.href = "data:application/octet-stream;base64," + bytesBase64;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
*Note for the sake of the post I changed the source path but I have verified the path does return a PDF, from a direct URL from azure blob storage.
For anyone who needs it, I solved this issue by creating a controller.
[HttpGet("{appId}/{formCode}")]
public FileContentResult DownloadForm(int appId, int formCode)
{
byte[] PdfData = Repo.GetFormBytes(appId, (Enums.FormType)formCode);
if (PdfData != null)
{
FileContentResult file = new FileContentResult(PdfData, "application/pdf");
file.FileDownloadName = Repo.MostRecentFileName;
return file;
}
else
return null;
}
Where GetFormBytes returns byte array output.ToArray() after it was edited. (See question).

Save byte as excel file on the server side

I have a method that returns a stream which will be written as an excel file on the clientside
Before getting to the client side I will like to write the file to a path and make sure
it is working.
How can I write bytes to a path on the c drive?
public async Task<IActionResult> GetTaxCodeMap(int fileId)
{
var result = await _myFileService.GetMyData(fileId);
if (result == null)
{
return NotFound("File does not exist.");
}
using (var memoryStream = new MemoryStream())
{
result.CopyTo(memoryStream);
var path = #"Workbk.xlsx";
var bytes = memoryStream.ToArray();
File.WriteAllBytes(path, bytes); //? Not working
//return File(memoryStream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"Workbk.xlsx");
}
return ok
}

Convert Xamarin.Forms.Image to Byte Array

Simple Question: I have a Xamarin.Forms.Image called "image". I need it to be a byte array. I feel like this has to be a simple thing but I can't find it on the internet anywhere.
var image = Xamarin.Forms.Image();
image.source = "my_image_source.png";
//Magic code
byte[] imgByteArray = ???
Unfortunately, the Image class does not expose methods to access the actual image once loaded.
You will need to read the Image from the file and convert it to the Stream you are expecting.
To prevent reading the image object twice you can set the stream read to your Image control.
var image = Xamarin.Forms.Image();
var assembly = this.GetType().GetTypeInfo().Assembly;
byte[] imgByteArray = null;
using (var s = assembly.GetManifestResourceStream("my_image_source.png"))
{
if (s != null)
{
var length = s.Length;
imgByteArray = new byte[length];
s.Read(buffer, 0, (int)length);
image.Source = ImageSource.FromStream(() => s);
}
}
// here imageByteArray will have the bytes from the image file or it will be null if the file was not loaded.
if (imgByteArray != null)
{
//use your data here.
}
Hope this helps.-
Update:
This code will require adding your my_image_source.png as part of the PCL as an embedded resource.

Send InkCanvas signature using WCF call

I have a Windows 10 UWP app that will run on Windows 10 Mobile. A requirement I have is to capture a signature from the user. So far, I am simply using an InkCanvas in XAML and have it wired up to my code behind.
I then have a button that when clicked, will take the signature on the InkCanvas and send it to the server via a WCF call. The server and WCF service is already existing. It takes in the signature image as a base64 serialized string.
I know how to get the base64 once I have either an image or a byte array. However, in my many hours of reading, I am finding that articles/examples were either written for WPF or Windows 8.1 and do not work on Windows 10 UWP. Also, of the examples that I have found that will work, it seems my only option is to save the signature to file as a GIF.
I see that I can call GetStrokes() like this
var strokeCollection = cvsSignature.InkPresenter.StrokeContainer.GetStrokes();
Which will return me a read only list of InkStroke. I guess I can iterate that list and build a byte array? How would I do that? It seems this is not efficient?
Otherwise, I thought I could just change the stream from a file stream to a memory stream but I guess either this is not possible or I am missing something. I am trying this
using (var inkMemStream = new MemoryStream())
{
await cvsSignature.InkPresenter.StrokeContainer.SaveAsync(inkMemStream);
}
But with this type of approach I get an exception that I cannot convert from System.IO.MemoryStream to Windows.Storage.Streams.IOutputStream
Thanks!
I found a way, if you don't want to save your InkCanvas to a file or as a GIF, to manipulate it in memory. I actually have three options below. Note, for some of this, you will need to add a reference to Win2D.uwp, which can be found on NuGet by searching that exact name. It is provided by Microsoft.
Convert the InkCanvas to a byte array:
private byte[] ConvertInkCanvasToByteArray()
{
//First, we need to get all of the strokes in the canvas
var canvasStrokes = myCanvas.InkPresenter.StrokeContainer.GetStrokes();
//Just as a check, make sure to only do work if there are actually strokes (ie not empty)
if (canvasStrokes.Count > 0)
{
var width = (int)myCanvas.ActualWidth;
var height = (int)myCanvas.ActualHeight;
var device = CanvasDevice.GetSharedDevice();
//Create a new renderTarget with the same width and height as myCanvas at 96dpi
var renderTarget = new CanvasRenderTarget(device, width,
height, 96);
using (var ds = renderTarget.CreateDrawingSession())
{
//This will clear the renderTarget with a clean slate of a white background.
ds.Clear(Windows.UI.Colors.White);
//Here is where we actually take the strokes from the canvas and draw them on the render target.
ds.DrawInk(myCanvas.InkPresenter.StrokeContainer.GetStrokes());
}
//Finally, this will return the render target as a byte array.
return renderTarget.GetPixelBytes();
}
else
{
return null;
}
}
If all you need is a byte array of the pixels, you are done. However, if you now want to generate an image, you use the WriteableBitmap to do so. Here are sync and async methods that can be called to do this.
private WriteableBitmap GetSignatureBitmapFull()
{
var bytes = ConvertInkCanvasToByteArray();
if (bytes != null)
{
var width = (int)cvsSignature.ActualWidth;
var height = (int)cvsSignature.ActualHeight;
var bmp = new WriteableBitmap(width, height);
using (var stream = bmp.PixelBuffer.AsStream())
{
stream.Write(bytes, 0, bytes.Length);
return bmp;
}
}
else
return null;
}
private async Task<WriteableBitmap> GetSignatureBitmapFullAsync()
{
var bytes = ConvertInkCanvasToByteArray();
if (bytes != null)
{
var width = (int)cvsSignature.ActualWidth;
var height = (int)cvsSignature.ActualHeight;
var bmp = new WriteableBitmap(width, height);
using (var stream = bmp.PixelBuffer.AsStream())
{
await stream.WriteAsync(bytes, 0, bytes.Length);
return bmp;
}
}
else
return null;
}
Finally, here is an async method that I am using to be able to do a crop on the bitmap if you want to do that. The key thing with this method is notice how with this approach, you don't have to get the pixel bytes as an array first. You just get the strokes from the canvas and then it is saved directly into a memory stream
private async Task<WriteableBitmap> GetSignatureBitmapCropped()
{
try
{
var canvasStrokes = cvsSignature.InkPresenter.StrokeContainer.GetStrokes();
if (canvasStrokes.Count > 0)
{
var bounds = cvsSignature.InkPresenter.StrokeContainer.BoundingRect;
var xOffset = (uint)Math.Round(bounds.X);
var yOffset = (uint)Math.Round(bounds.Y);
var pixelWidth = (int)Math.Round(bounds.Width);
var pixelHeight = (int)Math.Round(bounds.Height);
using (var memStream = new InMemoryRandomAccessStream())
{
await cvsSignature.InkPresenter.StrokeContainer.SaveAsync(memStream);
var decoder = await BitmapDecoder.CreateAsync(memStream);
var transform = new BitmapTransform();
var newBounds = new BitmapBounds();
newBounds.X = 0;
newBounds.Y = 0;
newBounds.Width = (uint)pixelWidth;
newBounds.Height = (uint)pixelHeight;
transform.Bounds = newBounds;
var pdp = await decoder.GetPixelDataAsync(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Straight,
transform,
ExifOrientationMode.IgnoreExifOrientation,
ColorManagementMode.DoNotColorManage);
var pixels = pdp.DetachPixelData();
var cropBmp = new WriteableBitmap(pixelWidth, pixelHeight);
using (var stream = cropBmp.PixelBuffer.AsStream())
{
await stream.WriteAsync(pixels, 0, pixels.Length);
}
return cropBmp;
}
}
else
{
return null;
}
}
catch (Exception ex)
{
return null;
}
}
Hope this helps provide some alternatives when using InkCanvas in Windows 10 Universal.
it seems my only option is to save the signature to file as a GIF.
GIF is not the only choice. The official sample just show GIF, but you can also save the InkStokes collection to JPG and PNG. Code as follows:
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
savePicker.FileTypeChoices.Add("Gif,JPG,PNG", new System.Collections.Generic.List<string> { ".jpg" ,".gif",".png"});
Windows.Storage.StorageFile file = await savePicker.PickSaveFileAsync();
But with this type of approach I get an exception that I cannot convert from System.IO.MemoryStream to Windows.Storage.Streams.IOutputStream
As you see, it cannot convert from System.IO.MemoryStream to Windows.Storage.Streams.IOutputStream directly. You need to read the image file you just saved as ImageSource. As you known how to get the base64 once you have either an image or a byte array. So what we just need is to get an image or a byte array from the image file.
Read the image file as byte array and then a memory stream as follows
if (inkCanvas.InkPresenter.StrokeContainer.GetStrokes().Count > 0)
{
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
savePicker.FileTypeChoices.Add("Gif,JPG,PNG", new System.Collections.Generic.List<string> { ".jpg", ".gif", ".png" });
Windows.Storage.StorageFile file = await savePicker.PickSaveFileAsync();
if (null != file)
{
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
await inkCanvas.InkPresenter.StrokeContainer.SaveAsync(stream);
}
using (IRandomAccessStream streamforread = await file.OpenAsync(FileAccessMode.Read))
{
WriteableBitmap inkimagesource = new WriteableBitmap(50, 50);
inkimagesource.SetSource(streamforread);
byte[] imageBuffer = inkimagesource.PixelBuffer.ToArray();
MemoryStream ms = new MemoryStream(imageBuffer);
}
}
}
Read the file as image source just need to change the file reading code as follows:
using (IRandomAccessStream streamforread = await file.OpenAsync(FileAccessMode.Read)
{
var bitmap = new BitmapImage();
bitmap.SetSource(streamforread);
}
I guess I can iterate that list and build a byte array? How would I do that? It seems this is not efficient?
It seems like currently there is no API can build the InkStroke collections as byte array directly. SaveAsync() method has already help you save the InkStroke collection to a stream, you can use code above for using it.

Passing bytes through Web API: why are these files different?

I'm storing bitmap images in an Azure blob store and delivering them to a .Net Micro Framework device. Because of memory limitations on the device I need to break the files into chunks and deliver them to the device where they are to be recombined onto the device's microSD card. I am having trouble with byte fidelity and am struggling to understand this pared down test.
I have a simple bitmap on azure: https://filebytetest9845.blob.core.windows.net/files/helloworld.bmp It is just a black and white bitmap of the words "Hello World".
Here's some test code I've written to sit in an ASP .Net Web API and read the bytes ready for breaking into chunks. But to test I just store the bytes to a local file.
[Route("api/testbytes/")]
[AcceptVerbs("GET", "POST")]
public void TestBytes()
{
var url = "https://filebytetest9845.blob.core.windows.net/files/helloworld.bmp";
var fileRequest = (HttpWebRequest) WebRequest.Create(url);
var fileResponse = (HttpWebResponse) fileRequest.GetResponse();
if (fileResponse.StatusCode == HttpStatusCode.OK)
{
if (fileResponse.ContentLength > 0)
{
var responseStream = fileResponse.GetResponseStream();
if (responseStream != null)
{
var contents = new byte[fileResponse.ContentLength];
responseStream.Read(contents, 0, (int) fileResponse.ContentLength);
if (!Directory.Exists(#"C:\Temp\Bytes\")) Directory.CreateDirectory(#"C:\Temp\Bytes\");
using (var fs = System.IO.File.Create(#"C:\Temp\Bytes\helloworldbytes.bmp"))
{
fs.Write(contents, 0, (int) fileResponse.ContentLength);
}
}
}
}
}
Here's the original bitmap:
And here's the version saved to disk:
As you can see they are different, but my code should just be saving a byte-for-byte copy. Why are they different?
Try this
var contents = new byte[fileResponse.ContentLength];
int readed = 0;
while (readed < fileResponse.ContentLength)
{
readed += responseStream.Read(contents, readed, (int)fileResponse.ContentLength - readed);
}
looks like it can't download whole image in single Read call and you have to recall it untill whole image is downloaded.
Atomosk is right - single Read call can't read whole response. If you are using .NET 4+, then you can use this code to read full response stream:
var fileResponse = (HttpWebResponse)fileRequest.GetResponse();
if (fileResponse.StatusCode == HttpStatusCode.OK)
{
var responseStream = fileResponse.GetResponseStream();
if (responseStream != null)
{
using (var ms = new MemoryStream())
{
responseStream.CopyTo(ms);
ms.Position = 0;
using (var fs = System.IO.File.Create(#"C:\Temp\Bytes\helloworldbytes.bmp"))
{
ms.CopyTo(fs);
}
}
}
}
Using this code you don't need to know Content Length since it is not always available.

Categories