After 3 hours of debugging and searching I have this.
public Stream ResizeImageToThumbnail(Stream imageStream, int width)
{
var image = Image.FromStream(imageStream);
var height = (width * image.Height) / image.Width;
var thumbnail = image.GetThumbnailImage(width, height, null, IntPtr.Zero);
using var thumbnailStream = new MemoryStream();
thumbnail.Save(thumbnailStream, ImageFormat.Jpeg);
return thumbnailStream;
}
Problem is, it returns exception
ArgumentException: Parameter is not valid.
at
var image = Image.FromStream(imageStream);
Parameter is file.OpenFileStream();
where file
is IFormFile
.
I'm out of ideas.
Edit
Requested code:
foreach (var item in model.UploadedImages) //item = IFormFile, image only allowed from HTML's end
{
using var ms = item.OpenReadStream();
_service.AttachImage(newId, ms, item.FileName);
ms.Position = 0;
_service.AttachThumb(newId, ms, item.FileName); //cannot access a closed stream exception
}
Edit 2
AttachThumb is using closed stream, returns exception (check comment). It appears that ResizeImageToThumbnail
returns closed stream.
public void AttachThumb(Guid id, Stream imageStream, string imageName)
{
Post post = GetPost(id);
ObjectId imageId = _gridFS.UploadFromStream(imageName, ResizeImageToThumbnail(imageStream, 640)); //cannot use closed stream
post.ImagesThumbs.Add(imageId.ToString());
var filter = Builders<Post>.Filter.Eq(x => x.Id, id);
var update = Builders<Post>.Update.Set("ImagesThumbs", post.ImagesThumbs);
_posts.UpdateOne(filter, update);
}
public Stream ResizeImageToThumbnail(Stream imageStream, int width)
{
var image = Image.FromStream(imageStream);
var height = (width * image.Height) / image.Width;
var thumbnail = image.GetThumbnailImage(width, height, null, IntPtr.Zero);
using var thumbnailStream = new MemoryStream();
thumbnail.Save(thumbnailStream, ImageFormat.Jpeg);
return thumbnailStream;
}
First copy the content of the file into a stream, then use that stream. Rewind the stream in between. Note that the two Attach* methods must not access the stream asynchronously. Otherwise you would have to create a second in-memory copy first.
foreach (var item in model.UploadedImages)
{
// Copy content in to stream
using (MemoryStream stream = new MemoryStream())
{
item.CopyTo(stream);
// Rewind and use
stream.Position = 0;
_service.AttachImage(newId, stream, item.FileName);
// Rewind and use
stream.Position = 0;
_service.AttachThumb(newId, stream, item.FileName);
}
}
As noted by user Jimi, you have to remove the using
clause from the ResizeImageToThumbnail
method. Otherwise the method returns an already disposed MemoryStream
:
public Stream ResizeImageToThumbnail(Stream imageStream, int width)
{
var image = Image.FromStream(imageStream);
var height = (width * image.Height) / image.Width;
var thumbnail = image.GetThumbnailImage(width, height, null, IntPtr.Zero);
// Bad: using var thumbnailStream = new MemoryStream();
// Remove using
var thumbnailStream = new MemoryStream();
thumbnail.Save(thumbnailStream, ImageFormat.Jpeg);
return thumbnailStream;
}
Then dispose the stream outside:
public void AttachThumb(Guid id, Stream imageStream, string imageName)
{
Post post = GetPost(id);
using var thumbnailStream = ResizeImageToThumbnail(imageStream, 640);
// rewind stream
thumbnailStream.position = 0;
ObjectId imageId = _gridFS.UploadFromStream(imageName, thumbnailStream);
...
You could improve the design of this by not having the ResizeImageToThumbnail
method create the stream but instead create the stream outside and pass it to ResizeImageToThumbnail
as a parameter. So the caller of ResizeImageToThumbnail
is then responsible for creating and disposing the stream.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.