From 13787cdafbce7e8dd118ffacda710e367c0b4e53 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 1 Oct 2021 16:20:55 +0800 Subject: [PATCH] Remove the custom method in HttpApi. --- .../Volo/Blogging/Files/FileUploadInputDto.cs | 5 +- .../Volo/Blogging/Files/IFileAppService.cs | 3 + .../Volo/Blogging/Files/FileAppService.cs | 51 ++++++++++++-- .../Volo/Blogging/Files/ImageFormatHelper.cs | 11 ++- .../BlogFilesClientProxy.Generated.cs | 9 +++ .../blogging-generate-proxy.json | 69 ++++++------------- .../Volo/Blogging/BlogFilesController.cs | 48 ++----------- .../Volo/Blogging/BloggingHttpApiModule.cs | 6 ++ .../Volo/Blogging/FileUploadResult.cs | 12 ---- .../Pages/Blogs/Posts/edit.js | 14 ++-- .../Pages/Blogs/Posts/new.js | 14 ++-- .../wwwroot/client-proxies/blogging-proxy.js | 12 +--- 12 files changed, 112 insertions(+), 142 deletions(-) delete mode 100644 modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/FileUploadResult.cs diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/FileUploadInputDto.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/FileUploadInputDto.cs index c0686835fd..b6c1a3546d 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/FileUploadInputDto.cs +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/FileUploadInputDto.cs @@ -1,13 +1,14 @@ using System.ComponentModel.DataAnnotations; +using Volo.Abp.Content; namespace Volo.Blogging.Files { public class FileUploadInputDto { [Required] - public byte[] Bytes { get; set; } + public IRemoteStreamContent File { get; set; } [Required] public string Name { get; set; } } -} \ No newline at end of file +} diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/IFileAppService.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/IFileAppService.cs index a949c848cd..b81b687536 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/IFileAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Files/IFileAppService.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Volo.Abp.Application.Services; +using Volo.Abp.Content; namespace Volo.Blogging.Files { @@ -7,6 +8,8 @@ namespace Volo.Blogging.Files { Task GetAsync(string name); + Task GetFileAsync(string name); + Task CreateAsync(FileUploadInputDto input); } } diff --git a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs index e7cc7047e0..5bb488106c 100644 --- a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs @@ -1,10 +1,12 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; +using System.Net.Mime; using System.Threading.Tasks; -using Microsoft.Extensions.Options; using Volo.Abp; using Volo.Abp.BlobStoring; +using Volo.Abp.Content; using Volo.Abp.Validation; using Volo.Blogging.Areas.Blog.Helpers; @@ -30,26 +32,61 @@ namespace Volo.Blogging.Files }; } + public virtual async Task GetFileAsync(string name) + { + var fileStream = await BlobContainer.GetAsync(name); + return new RemoteStreamContent(fileStream, name, GetByExtension(Path.GetExtension(name)), disposeStream: true); + } + + private static string GetByExtension(string extension) + { + extension = extension.RemovePreFix(".").ToLowerInvariant(); + + switch (extension) + { + case "png": + return "image/png"; + case "gif": + return "image/gif"; + case "jpg": + case "jpeg": + return "image/jpeg"; + + //TODO: Add other extensions too.. + + default: + return "application/octet-stream"; + } + } + public virtual async Task CreateAsync(FileUploadInputDto input) { - if (input.Bytes.IsNullOrEmpty()) + if (input.File == null) { - ThrowValidationException("Bytes of file can not be null or empty!", "Bytes"); + ThrowValidationException("Bytes of file can not be null or empty!", nameof(input.File)); } - if (input.Bytes.Length > BloggingWebConsts.FileUploading.MaxFileSize) + if (input.File.ContentLength > BloggingWebConsts.FileUploading.MaxFileSize) { throw new UserFriendlyException($"File exceeds the maximum upload size ({BloggingWebConsts.FileUploading.MaxFileSizeAsMegabytes} MB)!"); } - if (!ImageFormatHelper.IsValidImage(input.Bytes, FileUploadConsts.AllowedImageUploadFormats)) + var position = input.File.GetStream().Position; + + if (!ImageFormatHelper.IsValidImage(input.File.GetStream(), FileUploadConsts.AllowedImageUploadFormats)) { throw new UserFriendlyException("Invalid image format!"); } + // IsValidImage may change the position of the stream + if (input.File.GetStream().CanSeek) + { + input.File.GetStream().Position = position; + } + var uniqueFileName = GenerateUniqueFileName(Path.GetExtension(input.Name)); - await BlobContainer.SaveAsync(uniqueFileName, input.Bytes); + await BlobContainer.SaveAsync(uniqueFileName, input.File.GetStream()); return new FileUploadOutputDto { diff --git a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/ImageFormatHelper.cs b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/ImageFormatHelper.cs index 9afa5da07f..6a9521664d 100644 --- a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/ImageFormatHelper.cs +++ b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/ImageFormatHelper.cs @@ -7,20 +7,17 @@ namespace Volo.Blogging.Areas.Blog.Helpers { public class ImageFormatHelper { - public static ImageFormat GetImageRawFormat(byte[] fileBytes) + public static ImageFormat GetImageRawFormat(Stream stream) { - using (var memoryStream = new MemoryStream(fileBytes)) - { - return System.Drawing.Image.FromStream(memoryStream).RawFormat; - } + return System.Drawing.Image.FromStream(stream).RawFormat; } - public static bool IsValidImage(byte[] fileBytes, ICollection validFormats) + public static bool IsValidImage(Stream stream, ICollection validFormats) { // System.Drawing only works on windows => https://docs.microsoft.com/en-us/dotnet/api/system.drawing.image?view=net-5.0#remarks if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - var imageFormat = GetImageRawFormat(fileBytes); + var imageFormat = GetImageRawFormat(stream); return validFormats.Contains(imageFormat); } diff --git a/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/BlogFilesClientProxy.Generated.cs b/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/BlogFilesClientProxy.Generated.cs index c3188daf7e..e9b3880474 100644 --- a/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/BlogFilesClientProxy.Generated.cs +++ b/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/BlogFilesClientProxy.Generated.cs @@ -7,6 +7,7 @@ using Volo.Abp.Http.Modeling; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Client.ClientProxying; using Volo.Blogging.Files; +using Volo.Abp.Content; // ReSharper disable once CheckNamespace namespace Volo.Blogging.ClientProxies @@ -23,6 +24,14 @@ namespace Volo.Blogging.ClientProxies }); } + public virtual async Task GetFileAsync(string name) + { + return await RequestAsync(nameof(GetFileAsync), new ClientProxyRequestTypeValue + { + { typeof(string), name } + }); + } + public virtual async Task CreateAsync(FileUploadInputDto input) { return await RequestAsync(nameof(CreateAsync), new ClientProxyRequestTypeValue diff --git a/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/blogging-generate-proxy.json b/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/blogging-generate-proxy.json index 3b2916d49e..7e4ba7ef13 100644 --- a/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/blogging-generate-proxy.json +++ b/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/blogging-generate-proxy.json @@ -51,9 +51,9 @@ "allowAnonymous": null, "implementFrom": "Volo.Blogging.Files.IFileAppService" }, - "GetForWebAsyncByName": { - "uniqueName": "GetForWebAsyncByName", - "name": "GetForWebAsync", + "GetFileAsyncByName": { + "uniqueName": "GetFileAsyncByName", + "name": "GetFileAsync", "httpMethod": "GET", "url": "api/blogging/files/www/{name}", "supportedVersions": [], @@ -82,17 +82,17 @@ } ], "returnValue": { - "type": "Microsoft.AspNetCore.Mvc.FileResult", - "typeSimple": "Microsoft.AspNetCore.Mvc.FileResult" + "type": "Volo.Abp.Content.IRemoteStreamContent", + "typeSimple": "Volo.Abp.Content.IRemoteStreamContent" }, "allowAnonymous": null, - "implementFrom": "Volo.Blogging.BlogFilesController" + "implementFrom": "Volo.Blogging.Files.IFileAppService" }, "CreateAsyncByInput": { "uniqueName": "CreateAsyncByInput", "name": "CreateAsync", "httpMethod": "POST", - "url": "api/blogging/files", + "url": "api/blogging/files/images/upload", "supportedVersions": [], "parametersOnMethod": [ { @@ -107,60 +107,35 @@ "parameters": [ { "nameOnMethod": "input", - "name": "input", + "name": "File", "jsonName": null, - "type": "Volo.Blogging.Files.FileUploadInputDto", - "typeSimple": "Volo.Blogging.Files.FileUploadInputDto", + "type": "Volo.Abp.Content.IRemoteStreamContent", + "typeSimple": "Volo.Abp.Content.IRemoteStreamContent", "isOptional": false, "defaultValue": null, "constraintTypes": null, - "bindingSourceId": "Body", - "descriptorName": "" - } - ], - "returnValue": { - "type": "Volo.Blogging.Files.FileUploadOutputDto", - "typeSimple": "Volo.Blogging.Files.FileUploadOutputDto" - }, - "allowAnonymous": null, - "implementFrom": "Volo.Blogging.Files.IFileAppService" - }, - "UploadImageByFile": { - "uniqueName": "UploadImageByFile", - "name": "UploadImage", - "httpMethod": "POST", - "url": "api/blogging/files/images/upload", - "supportedVersions": [], - "parametersOnMethod": [ - { - "name": "file", - "typeAsString": "Microsoft.AspNetCore.Http.IFormFile, Microsoft.AspNetCore.Http.Features", - "type": "Microsoft.AspNetCore.Http.IFormFile", - "typeSimple": "Microsoft.AspNetCore.Http.IFormFile", - "isOptional": false, - "defaultValue": null - } - ], - "parameters": [ + "bindingSourceId": "FormFile", + "descriptorName": "input" + }, { - "nameOnMethod": "file", - "name": "file", + "nameOnMethod": "input", + "name": "Name", "jsonName": null, - "type": "Microsoft.AspNetCore.Http.IFormFile", - "typeSimple": "Microsoft.AspNetCore.Http.IFormFile", + "type": "System.String", + "typeSimple": "string", "isOptional": false, "defaultValue": null, "constraintTypes": null, - "bindingSourceId": "FormFile", - "descriptorName": "" + "bindingSourceId": "ModelBinding", + "descriptorName": "input" } ], "returnValue": { - "type": "Microsoft.AspNetCore.Mvc.JsonResult", - "typeSimple": "Microsoft.AspNetCore.Mvc.JsonResult" + "type": "Volo.Blogging.Files.FileUploadOutputDto", + "typeSimple": "Volo.Blogging.Files.FileUploadOutputDto" }, "allowAnonymous": null, - "implementFrom": "Volo.Blogging.BlogFilesController" + "implementFrom": "Volo.Blogging.Files.IFileAppService" } } }, diff --git a/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogFilesController.cs b/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogFilesController.cs index f71a0af6c2..12bedc3e6c 100644 --- a/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogFilesController.cs +++ b/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogFilesController.cs @@ -1,11 +1,8 @@ -using System.IO; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; +using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp; using Volo.Abp.AspNetCore.Mvc; -using Volo.Abp.Http; -using Volo.Blogging.Areas.Blog.Models; +using Volo.Abp.Content; using Volo.Blogging.Files; namespace Volo.Blogging @@ -31,51 +28,16 @@ namespace Volo.Blogging [HttpGet] [Route("www/{name}")] - public async Task GetForWebAsync(string name) //TODO: output cache would be good + public async Task GetFileAsync(string name) { - var file = await _fileAppService.GetAsync(name); - return File( - file.Bytes, - MimeTypes.GetByExtension(Path.GetExtension(name)) - ); + return await _fileAppService.GetFileAsync(name); } [HttpPost] + [Route("images/upload")] public Task CreateAsync(FileUploadInputDto input) { return _fileAppService.CreateAsync(input); } - - [HttpPost] - [Route("images/upload")] - public async Task UploadImage(IFormFile file) - { - //TODO: localize exception messages - - if (file == null) - { - throw new UserFriendlyException("No file found!"); - } - - if (file.Length <= 0) - { - throw new UserFriendlyException("File is empty!"); - } - - if (!file.ContentType.Contains("image")) - { - throw new UserFriendlyException("Not a valid image!"); - } - - var output = await _fileAppService.CreateAsync( - new FileUploadInputDto - { - Bytes = file.GetAllBytes(), - Name = file.FileName - } - ); - - return Json(new FileUploadResult(output.WebUrl)); - } } } diff --git a/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BloggingHttpApiModule.cs b/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BloggingHttpApiModule.cs index 3365063299..d7702f300b 100644 --- a/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BloggingHttpApiModule.cs +++ b/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BloggingHttpApiModule.cs @@ -4,6 +4,7 @@ using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Blogging.Localization; using Microsoft.Extensions.DependencyInjection; +using Volo.Blogging.Files; namespace Volo.Blogging { @@ -28,6 +29,11 @@ namespace Volo.Blogging .Get() .AddBaseTypes(typeof(AbpUiResource)); }); + + Configure(options => + { + options.ConventionalControllers.FormBodyBindingIgnoredTypes.Add(typeof(FileUploadInputDto)); + }); } } } diff --git a/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/FileUploadResult.cs b/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/FileUploadResult.cs deleted file mode 100644 index 9cc491ed37..0000000000 --- a/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/FileUploadResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Volo.Blogging.Areas.Blog.Models -{ - public class FileUploadResult - { - public string FileUrl { get; set; } - - public FileUploadResult(string fileUrl) - { - FileUrl = fileUrl; - } - } -} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/edit.js b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/edit.js index f90bd744cd..b5223f6790 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/edit.js +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/edit.js @@ -13,15 +13,15 @@ $(function () { var $postFormSubmitButton = $('#PostFormSubmitButton'); var setCoverImage = function (file) { - $postCoverImage.val(file.fileUrl); - $coverImage.attr('src', file.fileUrl); + $postCoverImage.val(file.webUrl); + $coverImage.attr('src', file.webUrl); $postFormSubmitButton.removeAttr('disabled'); }; var uploadCoverImage = function (file) { var formData = new FormData(); formData.append('file', file); - + formData.append('name', file.name); $.ajax({ type: 'POST', url: '/api/blogging/files/images/upload', @@ -63,7 +63,7 @@ $(function () { var uploadImage = function (file, callbackFn) { var formData = new FormData(); formData.append('file', file); - + formData.append('name', file.name); $.ajax({ type: 'POST', url: '/api/blogging/files/images/upload', @@ -71,7 +71,7 @@ $(function () { contentType: false, processData: false, success: function (response) { - callbackFn(response.fileUrl); + callbackFn(response.webUrl); }, }); }; @@ -87,8 +87,8 @@ $(function () { addImageBlobHook: function (blob, callback, source) { var imageAltText = blob.name; - uploadImage(blob, function (fileUrl) { - callback(fileUrl, imageAltText); + uploadImage(blob, function (webUrl) { + callback(webUrl, imageAltText); }); }, }, diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/new.js b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/new.js index 48031bd8bc..c793a5312b 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/new.js +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/new.js @@ -13,8 +13,8 @@ $(function () { var $postFormSubmitButton = $('#PostFormSubmitButton'); var setCoverImage = function (file) { - $postCoverImage.val(file.fileUrl); - $coverImage.attr('src', file.fileUrl); + $postCoverImage.val(file.webUrl); + $coverImage.attr('src', file.webUrl); $coverImage.show(); $postFormSubmitButton.removeAttr('disabled'); }; @@ -22,7 +22,7 @@ $(function () { var uploadCoverImage = function (file) { var formData = new FormData(); formData.append('file', file); - + formData.append('name', file.name); $.ajax({ type: 'POST', url: '/api/blogging/files/images/upload', @@ -47,7 +47,7 @@ $(function () { var uploadImage = function (file, callbackFn) { var formData = new FormData(); formData.append('file', file); - + formData.append('name', file.name); $.ajax({ type: 'POST', url: '/api/blogging/files/images/upload', @@ -55,7 +55,7 @@ $(function () { contentType: false, processData: false, success: function (response) { - callbackFn(response.fileUrl); + callbackFn(response.webUrl); }, }); }; @@ -70,8 +70,8 @@ $(function () { addImageBlobHook: function (blob, callback, source) { var imageAltText = blob.name; - uploadImage(blob, function (fileUrl) { - callback(fileUrl, imageAltText); + uploadImage(blob, function (webUrl) { + callback(webUrl, imageAltText); }); }, }, diff --git a/modules/blogging/src/Volo.Blogging.Web/wwwroot/client-proxies/blogging-proxy.js b/modules/blogging/src/Volo.Blogging.Web/wwwroot/client-proxies/blogging-proxy.js index 3b0905a0d5..c2872c74bf 100644 --- a/modules/blogging/src/Volo.Blogging.Web/wwwroot/client-proxies/blogging-proxy.js +++ b/modules/blogging/src/Volo.Blogging.Web/wwwroot/client-proxies/blogging-proxy.js @@ -18,7 +18,7 @@ }, ajaxParams)); }; - volo.blogging.blogFiles.getForWeb = function(name, ajaxParams) { + volo.blogging.blogFiles.getFile = function(name, ajaxParams) { return abp.ajax($.extend(true, { url: abp.appPath + 'api/blogging/files/www/' + name + '', type: 'GET' @@ -27,15 +27,7 @@ volo.blogging.blogFiles.create = function(input, ajaxParams) { return abp.ajax($.extend(true, { - url: abp.appPath + 'api/blogging/files', - type: 'POST', - data: JSON.stringify(input) - }, ajaxParams)); - }; - - volo.blogging.blogFiles.uploadImage = function(file, ajaxParams) { - return abp.ajax($.extend(true, { - url: abp.appPath + 'api/blogging/files/images/upload', + url: abp.appPath + 'api/blogging/files/images/upload' + abp.utils.buildQueryString([{ name: 'name', value: input.name }]) + '', type: 'POST' }, ajaxParams)); };