/
UploadStorageExtensions.cs
358 lines (311 loc) · 15.3 KB
/
UploadStorageExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
using System.IO;
namespace Serenity.Web;
/// <summary>
/// Extension methods for <see cref="IUploadStorage"/> and related classes
/// </summary>
public static class UploadStorageExtensions
{
/// <summary>
/// Gets thumbnail URL for the file path
/// </summary>
/// <param name="uploadStorage">Upload storage</param>
/// <param name="path">Path</param>
/// <returns></returns>
public static string GetThumbnailUrl(this IUploadStorage uploadStorage, string path)
{
if (string.IsNullOrEmpty(path))
return null;
var thumb = UploadPathHelper.GetThumbnailName(path);
return uploadStorage.GetFileUrl(thumb);
}
/// <summary>
/// Copies a temporary file to its target location
/// </summary>
/// <param name="uploadStorage">Upload storage</param>
/// <param name="options">Copy options</param>
/// <exception cref="ArgumentNullException">uploadStorage is null</exception>
public static CopyTemporaryFileResult CopyTemporaryFile(this IUploadStorage uploadStorage,
CopyTemporaryFileOptions options)
{
if (uploadStorage is null)
throw new ArgumentNullException(nameof(uploadStorage));
if (options is null)
throw new ArgumentNullException(nameof(options));
long size = uploadStorage.GetFileSize(options.TemporaryFile);
string path = PathHelper.ToUrl(UploadFormatting.FormatFilename(options));
path = uploadStorage.CopyFrom(uploadStorage, options.TemporaryFile, path, OverwriteOption.AutoRename);
bool hasThumbnail = uploadStorage.FileExists(UploadPathHelper.GetThumbnailName(options.TemporaryFile));
var result = new CopyTemporaryFileResult()
{
FileSize = size,
Path = path,
OriginalName = options.OriginalName,
HasThumbnail = hasThumbnail
};
options.FilesToDelete?.RegisterNewFile(path);
options.FilesToDelete?.RegisterOldFile(options.TemporaryFile);
return result;
}
/// <summary>
/// Reads all file bytes
/// </summary>
/// <param name="uploadStorage">Upload storage</param>
/// <param name="path">File path</param>
/// <exception cref="ArgumentNullException">Upload storage is null</exception>
public static byte[] ReadAllFileBytes(this IUploadStorage uploadStorage, string path)
{
if (uploadStorage is null)
throw new ArgumentNullException(nameof(uploadStorage));
using var ms = new MemoryStream();
using (var fs = uploadStorage.OpenFile(path))
fs.CopyTo(ms);
return ms.ToArray();
}
/// <summary>
/// Gets original name of a file
/// </summary>
/// <param name="uploadStorage">Upload storage</param>
/// <param name="path">File path</param>
/// <exception cref="ArgumentNullException">uploadStorage is null</exception>
public static string GetOriginalName(this IUploadStorage uploadStorage, string path)
{
if (uploadStorage is null)
throw new ArgumentNullException(nameof(uploadStorage));
var metadata = uploadStorage.GetFileMetadata(path);
if (metadata != null &&
metadata.TryGetValue(FileMetadataKeys.OriginalName, out string originalName))
return originalName;
return null;
}
/// <summary>
/// Sets original name for a file
/// </summary>
/// <param name="uploadStorage">Upload storage</param>
/// <param name="path">File path</param>
/// <param name="originalName">Original name</param>
/// <exception cref="ArgumentNullException">Upload storage is null</exception>
public static void SetOriginalName(this IUploadStorage uploadStorage, string path, string originalName)
{
if (uploadStorage is null)
throw new ArgumentNullException(nameof(uploadStorage));
var metadata = new Dictionary<string, string>()
{
[FileMetadataKeys.OriginalName] = originalName
};
uploadStorage.SetFileMetadata(path, metadata, overwriteAll: false);
}
/// <summary>
/// Scales an image and saves it to an upload storage file
/// </summary>
/// <param name="image">Source image</param>
/// <param name="imageProcessor">Image processor</param>
/// <param name="width">Target width</param>
/// <param name="height">Target height</param>
/// <param name="mode">Scale mode</param>
/// <param name="backgroundColor">Pad color</param>
/// <param name="mimeType">Mime type of target image file</param>
/// <param name="encoderParams">Encoder parameters for target image</param>
/// <param name="uploadStorage">Upload storage</param>
/// <param name="path">Path</param>
/// <param name="overwrite">Overwrite option</param>
/// <exception cref="ArgumentNullException">One of inputs is null</exception>
/// <exception cref="ArgumentOutOfRangeException">Width or height is less than zero</exception>
public static string ScaleImageAs(object image, IImageProcessor imageProcessor,
int width, int height, ImageScaleMode mode, string backgroundColor,
string mimeType, ImageEncoderParams encoderParams,
IUploadStorage uploadStorage, string path, OverwriteOption overwrite)
{
if (image is null)
throw new ArgumentNullException(nameof(image));
if (imageProcessor is null)
throw new ArgumentNullException(nameof(imageProcessor));
if (uploadStorage is null)
throw new ArgumentNullException(nameof(uploadStorage));
if (width < 0)
throw new ArgumentOutOfRangeException(nameof(width));
if (height < 0)
throw new ArgumentOutOfRangeException(nameof(height));
if (width == 0 && height == 0)
throw new ArgumentOutOfRangeException(nameof(width));
var scaledImage = imageProcessor.Scale(image, width, height, mode, backgroundColor, inplace: false);
try
{
using var ms = new MemoryStream();
imageProcessor.Save(scaledImage, ms, mimeType, encoderParams);
ms.Seek(0, SeekOrigin.Begin);
return uploadStorage.WriteFile(path, ms, overwrite);
}
finally
{
(scaledImage as IDisposable)?.Dispose();
}
}
/// <summary>
/// Scales the temporary image with provided upload image options if required
/// based on the options and saves the result to the target upload storage file
/// </summary>
/// <param name="image">Image object</param>
/// <param name="imageProcessor">Image processor</param>
/// <param name="options">Image upload options</param>
/// <param name="uploadStorage">Upload storage</param>
/// <param name="temporaryFile">Temporary input file</param>
/// <param name="overwrite">Overwrite </param>
/// <returns>The resulting image file path</returns>
/// <exception cref="ArgumentNullException">image or options is null</exception>
public static string ScaleImage(object image,
IImageProcessor imageProcessor, IUploadImageOptions options,
IUploadStorage uploadStorage, string temporaryFile, OverwriteOption overwrite)
{
if (image is null)
throw new ArgumentNullException(nameof(image));
if (options is null)
throw new ArgumentNullException(nameof(options));
if (string.IsNullOrEmpty(temporaryFile))
throw new ArgumentNullException(nameof(temporaryFile));
var (imageWidth, imageHeight) = imageProcessor.GetImageSize(image);
var scaleSmaller = options.ScaleSmaller == true;
var baseFile = Path.ChangeExtension(temporaryFile, null);
if ((options.ScaleWidth > 0 || options.ScaleHeight > 0) &&
(options.ScaleWidth != imageWidth || options.ScaleHeight != imageHeight) &&
((options.ScaleWidth > 0 && (scaleSmaller || options.ScaleWidth < imageWidth)) ||
(options.ScaleHeight > 0 && (scaleSmaller || options.ScaleHeight < imageHeight))))
{
var originalName = uploadStorage.GetOriginalName(temporaryFile);
temporaryFile = ScaleImageAs(image, imageProcessor,
options.ScaleWidth, options.ScaleHeight, options.ScaleMode, options.ScaleBackColor,
mimeType: "image/jpeg", new ImageEncoderParams { Quality = options.ScaleQuality },
uploadStorage, baseFile + ".jpg", overwrite);
if (!string.IsNullOrEmpty(originalName))
uploadStorage.SetOriginalName(temporaryFile, Path.ChangeExtension(originalName, ".jpg"));
}
return temporaryFile;
}
/// <summary>
/// Creates the default thumbnail for image if the size is provided
/// in the upload image options (ThumbWidth and ThumbHeight >= 0) and saves it to the target upload storage file
/// </summary>
/// <param name="image">Image</param>
/// <param name="imageProcessor">Image processor</param>
/// <param name="options">Upload image options</param>
/// <param name="uploadStorage">Upload storage</param>
/// <param name="temporaryFile">Input temporary file</param>
/// <param name="overwrite">Overwrite option</param>
/// <exception cref="ArgumentNullException">image, options or temporaryFile is null</exception>
public static string CreateDefaultThumb(object image,
IImageProcessor imageProcessor, IUploadImageOptions options,
IUploadStorage uploadStorage, string temporaryFile, OverwriteOption overwrite)
{
if (image is null)
throw new ArgumentNullException(nameof(image));
if (options is null)
throw new ArgumentNullException(nameof(options));
if (string.IsNullOrEmpty(temporaryFile))
throw new ArgumentNullException(nameof(temporaryFile));
if ((options.ThumbWidth > 0 || options.ThumbHeight > 0) &&
(options.ThumbWidth >= 0 && options.ThumbHeight >= 0))
{
return ScaleImageAs(image, imageProcessor,
options.ThumbWidth, options.ThumbHeight, options.ThumbMode, options.ThumbBackColor,
mimeType: "image/jpeg", new ImageEncoderParams { Quality = options.ThumbQuality },
uploadStorage, Path.ChangeExtension(temporaryFile, null) + "_t.jpg", overwrite);
}
return null;
}
/// <summary>
/// Creates additional thumbs if specified in the upload image options,
/// and saves them to the target upload storage
/// </summary>
/// <param name="image">Image</param>
/// <param name="imageProcessor">Image processor</param>
/// <param name="options">Upload image options</param>
/// <param name="uploadStorage">Target upload storage</param>
/// <param name="temporaryFile">Input temporary file</param>
/// <param name="overwrite">Overwrite option</param>
/// <exception cref="ArgumentNullException">image, options or temporaryFile is null</exception>
/// <exception cref="ArgumentOutOfRangeException">options.ThumbSizes contains invalid values</exception>
public static void CreateAdditionalThumbs(object image,
IImageProcessor imageProcessor, IUploadImageOptions options,
IUploadStorage uploadStorage, string temporaryFile, OverwriteOption overwrite)
{
if (image is null)
throw new ArgumentNullException(nameof(image));
if (options is null)
throw new ArgumentNullException(nameof(options));
if (string.IsNullOrEmpty(temporaryFile))
throw new ArgumentNullException(nameof(temporaryFile));
var thumbSizes = options.ThumbSizes.TrimToNull();
if (thumbSizes is null)
return;
var baseFile = Path.ChangeExtension(temporaryFile, null);
foreach (var sizeStr in thumbSizes.Replace(";", ",", StringComparison.Ordinal).Split([',']))
{
var dims = sizeStr.ToUpperInvariant().Split(['X']);
if (dims.Length != 2 ||
!int.TryParse(dims[0], out int w) ||
!int.TryParse(dims[1], out int h) ||
w < 0 ||
h < 0 ||
(w == 0 && h == 0))
throw new ArgumentOutOfRangeException(nameof(thumbSizes));
var thumbFile = baseFile + "_t" + w.ToInvariant() + "x" + h.ToInvariant() + ".jpg";
ScaleImageAs(image, imageProcessor,
w, h, options.ThumbMode, options.ThumbBackColor,
mimeType: "image/jpeg", new ImageEncoderParams { Quality = options.ThumbQuality },
uploadStorage, thumbFile, overwrite);
}
}
/// <summary>
/// Depending on the image upload options, scales image, creates default and
/// additional thumbs and saves them to the upload storage files.
/// </summary>
/// <param name="image">Input image</param>
/// <param name="imageProcessor">Image processor</param>
/// <param name="options">Upload image options</param>
/// <param name="uploadStorage">Upload storage</param>
/// <param name="temporaryFile">Temporary file</param>
/// <param name="overwrite">Overwrite option</param>
/// <returns>Temporary file</returns>
public static string ScaleImageAndCreateAllThumbs(object image,
IImageProcessor imageProcessor, IUploadImageOptions options,
IUploadStorage uploadStorage, string temporaryFile, OverwriteOption overwrite)
{
var orgTempFile = temporaryFile;
temporaryFile = ScaleImage(image, imageProcessor, options, uploadStorage, temporaryFile, overwrite);
CreateDefaultThumb(image, imageProcessor, options, uploadStorage, orgTempFile, overwrite);
CreateAdditionalThumbs(image, imageProcessor, options, uploadStorage, orgTempFile, overwrite);
return temporaryFile;
}
/// <summary>
/// Copies a file from another upload storage and returns the resulting file path
/// </summary>
/// <param name="targetStorage">Target upload storage</param>
/// <param name="sourceStorage">Source upload storage</param>
/// <param name="sourcePath">Source file path</param>
/// <param name="targetPath">Target file path</param>
/// <param name="autoRename">If a file exists at target, true to auto rename,
/// false to raise an error, and null to overwrite</param>
[Obsolete("Please use the method with OverwriteOption enumeration")]
public static string CopyFrom(this IUploadStorage targetStorage, IUploadStorage sourceStorage, string sourcePath, string targetPath, bool? autoRename)
{
return targetStorage.CopyFrom(sourceStorage, sourcePath, targetPath, ToOverwriteOption(autoRename));
}
/// <summary>
/// Writes a file
/// </summary>
/// <param name="uploadStorage">Upload storage</param>
/// <param name="path">File path</param>
/// <param name="source">Source stream</param>
/// <param name="autoRename">If a file exists at target, true to auto rename,
/// false to raise an error, and null to overwrite</param>
[Obsolete("Please use the method with OverwriteOption enumeration")]
public static string WriteFile(this IUploadStorage uploadStorage, string path, Stream source, bool? autoRename)
{
return uploadStorage.WriteFile(path, source, ToOverwriteOption(autoRename));
}
private static OverwriteOption ToOverwriteOption(bool? autoRename)
{
return autoRename == null ? OverwriteOption.Overwrite :
autoRename == true ? OverwriteOption.AutoRename :
OverwriteOption.Disallowed;
}
}