Skip to content

Commit 259347d

Browse files
Add missing SaveAsync method
1 parent 9b9e134 commit 259347d

File tree

3 files changed

+90
-8
lines changed

3 files changed

+90
-8
lines changed

src/ImageSharp/Advanced/AdvancedImageExtensions.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ public static class AdvancedImageExtensions
2323
/// </summary>
2424
/// <param name="source">The source image.</param>
2525
/// <param name="filePath">The target file path to save the image to.</param>
26-
/// <returns>The matching encoder.</returns>
26+
/// <exception cref="ArgumentNullException">The file path is null.</exception>
27+
/// <exception cref="NotSupportedException">No encoder available for provided path.</exception>
28+
/// <returns>The matching <see cref="IImageEncoder"/>.</returns>
2729
public static IImageEncoder DetectEncoder(this Image source, string filePath)
2830
{
2931
Guard.NotNull(filePath, nameof(filePath));

src/ImageSharp/ImageExtensions.cs

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,29 @@ namespace SixLabors.ImageSharp
1818
public static partial class ImageExtensions
1919
{
2020
/// <summary>
21-
/// Writes the image to the given stream using the currently loaded image format.
21+
/// Writes the image to the given file path using an encoder detected from the path.
2222
/// </summary>
2323
/// <param name="source">The source image.</param>
2424
/// <param name="path">The file path to save the image to.</param>
2525
/// <exception cref="ArgumentNullException">The path is null.</exception>
26+
/// <exception cref="NotSupportedException">No encoder available for provided path.</exception>
2627
public static void Save(this Image source, string path)
2728
=> source.Save(path, source.DetectEncoder(path));
2829

2930
/// <summary>
30-
/// Writes the image to the given stream using the currently loaded image format.
31+
/// Writes the image to the given file path using an encoder detected from the path.
3132
/// </summary>
3233
/// <param name="source">The source image.</param>
3334
/// <param name="path">The file path to save the image to.</param>
3435
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
3536
/// <exception cref="ArgumentNullException">The path is null.</exception>
37+
/// <exception cref="NotSupportedException">No encoder available for provided path.</exception>
3638
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
3739
public static Task SaveAsync(this Image source, string path, CancellationToken cancellationToken = default)
3840
=> source.SaveAsync(path, source.DetectEncoder(path), cancellationToken);
3941

4042
/// <summary>
41-
/// Writes the image to the given stream using the currently loaded image format.
43+
/// Writes the image to the given file path using the given image encoder.
4244
/// </summary>
4345
/// <param name="source">The source image.</param>
4446
/// <param name="path">The file path to save the image to.</param>
@@ -56,7 +58,7 @@ public static void Save(this Image source, string path, IImageEncoder encoder)
5658
}
5759

5860
/// <summary>
59-
/// Writes the image to the given stream using the currently loaded image format.
61+
/// Writes the image to the given file path using the given image encoder.
6062
/// </summary>
6163
/// <param name="source">The source image.</param>
6264
/// <param name="path">The file path to save the image to.</param>
@@ -73,12 +75,15 @@ public static async Task SaveAsync(
7375
{
7476
Guard.NotNull(path, nameof(path));
7577
Guard.NotNull(encoder, nameof(encoder));
76-
using Stream fs = source.GetConfiguration().FileSystem.Create(path);
77-
await source.SaveAsync(fs, encoder, cancellationToken).ConfigureAwait(false);
78+
79+
using (Stream fs = source.GetConfiguration().FileSystem.Create(path))
80+
{
81+
await source.SaveAsync(fs, encoder, cancellationToken).ConfigureAwait(false);
82+
}
7883
}
7984

8085
/// <summary>
81-
/// Writes the image to the given stream using the currently loaded image format.
86+
/// Writes the image to the given stream using the given image format.
8287
/// </summary>
8388
/// <param name="source">The source image.</param>
8489
/// <param name="stream">The stream to save the image to.</param>
@@ -115,6 +120,50 @@ public static void Save(this Image source, Stream stream, IImageFormat format)
115120
source.Save(stream, encoder);
116121
}
117122

123+
/// <summary>
124+
/// Writes the image to the given stream using the given image format.
125+
/// </summary>
126+
/// <param name="source">The source image.</param>
127+
/// <param name="stream">The stream to save the image to.</param>
128+
/// <param name="format">The format to save the image in.</param>
129+
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
130+
/// <exception cref="ArgumentNullException">The stream is null.</exception>
131+
/// <exception cref="ArgumentNullException">The format is null.</exception>
132+
/// <exception cref="NotSupportedException">The stream is not writable.</exception>
133+
/// <exception cref="NotSupportedException">No encoder available for provided format.</exception>
134+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
135+
public static Task SaveAsync(
136+
this Image source,
137+
Stream stream,
138+
IImageFormat format,
139+
CancellationToken cancellationToken = default)
140+
{
141+
Guard.NotNull(stream, nameof(stream));
142+
Guard.NotNull(format, nameof(format));
143+
144+
if (!stream.CanWrite)
145+
{
146+
throw new NotSupportedException("Cannot write to the stream.");
147+
}
148+
149+
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
150+
151+
if (encoder is null)
152+
{
153+
var sb = new StringBuilder();
154+
sb.AppendLine("No encoder was found for the provided mime type. Registered encoders include:");
155+
156+
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
157+
{
158+
sb.AppendFormat(" - {0} : {1}{2}", val.Key.Name, val.Value.GetType().Name, Environment.NewLine);
159+
}
160+
161+
throw new NotSupportedException(sb.ToString());
162+
}
163+
164+
return source.SaveAsync(stream, encoder, cancellationToken);
165+
}
166+
118167
/// <summary>
119168
/// Returns a Base64 encoded string from the given image.
120169
/// The result is prepended with a Data URI <see href="https://en.wikipedia.org/wiki/Data_URI_scheme"/>

tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,37 @@ public async Task SetEncoding()
7272
}
7373
}
7474

75+
[Theory]
76+
[InlineData("test.png", "image/png")]
77+
[InlineData("test.tga", "image/tga")]
78+
[InlineData("test.bmp", "image/bmp")]
79+
[InlineData("test.jpg", "image/jpeg")]
80+
[InlineData("test.gif", "image/gif")]
81+
public async Task SaveStreamWithMime(string filename, string mimeType)
82+
{
83+
using (var image = new Image<Rgba32>(5, 5))
84+
{
85+
string ext = Path.GetExtension(filename);
86+
IImageFormat format = image.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
87+
Assert.Equal(mimeType, format.DefaultMimeType);
88+
89+
using (var stream = new MemoryStream())
90+
{
91+
var asyncStream = new AsyncStreamWrapper(stream, () => false);
92+
await image.SaveAsync(asyncStream, format);
93+
94+
stream.Position = 0;
95+
96+
(Image Image, IImageFormat Format) imf = await Image.LoadWithFormatAsync(stream);
97+
98+
Assert.Equal(format, imf.Format);
99+
Assert.Equal(mimeType, imf.Format.DefaultMimeType);
100+
101+
imf.Image.Dispose();
102+
}
103+
}
104+
}
105+
75106
[Fact]
76107
public async Task ThrowsWhenDisposed()
77108
{

0 commit comments

Comments
 (0)