mirror of https://github.com/abpframework/abp.git
17 changed files with 185 additions and 167 deletions
@ -0,0 +1,14 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Blogging |
|||
{ |
|||
public class BloggingWebConsts |
|||
{ |
|||
public class FileUploading |
|||
{ |
|||
public const int MaxFileSize = 5242880; //5MB
|
|||
|
|||
public static int MaxFileSizeAsMegabytes => Convert.ToInt32((MaxFileSize / 1024f) / 1024f); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace Volo.Blogging.Files |
|||
{ |
|||
public class FileUploadInputDto |
|||
{ |
|||
[Required] |
|||
public byte[] Bytes { get; set; } |
|||
|
|||
[Required] |
|||
public string Name { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Blogging.Files |
|||
{ |
|||
public class FileUploadOutputDto |
|||
{ |
|||
public string Url { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace Volo.Blogging.Files |
|||
{ |
|||
public interface IFileAppService : IApplicationService |
|||
{ |
|||
Task<FileUploadOutputDto> UploadAsync(FileUploadInputDto input); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
namespace Volo.Blogging.Files |
|||
{ |
|||
/* TODO: |
|||
* - It is not to have different options for all different modules. We should find a more generic way. |
|||
* - Actually, it is not good to assume to save to a local folder. Instead, use file storage once implemented. |
|||
*/ |
|||
public class BlogFileOptions |
|||
{ |
|||
public string FileUploadLocalFolder { get; set; } |
|||
|
|||
public string FileUploadUrlRoot { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.Validation; |
|||
using Volo.Blogging.Areas.Blog.Helpers; |
|||
|
|||
namespace Volo.Blogging.Files |
|||
{ |
|||
public class FileAppService : ApplicationService, IFileAppService |
|||
{ |
|||
public BlogFileOptions Options { get; } |
|||
|
|||
public FileAppService(IOptions<BlogFileOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public virtual Task<FileUploadOutputDto> UploadAsync(FileUploadInputDto input) |
|||
{ |
|||
if (input.Bytes.IsNullOrEmpty()) |
|||
{ |
|||
ThrowValidationException("Bytes can not be null or empty!", "Bytes"); |
|||
} |
|||
|
|||
if (input.Bytes.Length > BloggingWebConsts.FileUploading.MaxFileSize) |
|||
{ |
|||
throw new UserFriendlyException($"File exceeds the maximum upload size ({BloggingWebConsts.FileUploading.MaxFileSizeAsMegabytes} MB)!"); |
|||
} |
|||
|
|||
if (!ImageFormatHelper.IsValidImage(input.Bytes, FileUploadConsts.AllowedImageUploadFormats)) |
|||
{ |
|||
throw new UserFriendlyException("Not a valid image format!"); |
|||
} |
|||
|
|||
var uniqueFileName = GenerateUniqueFileName(Path.GetExtension(input.Name)); |
|||
var filePath = Path.Combine(Options.FileUploadLocalFolder, uniqueFileName); |
|||
|
|||
File.WriteAllBytes(filePath, input.Bytes); //TODO: Previously was using WriteAllBytesAsync, but it's only in .netcore.
|
|||
|
|||
return Task.FromResult(new FileUploadOutputDto |
|||
{ |
|||
Url = Options.FileUploadUrlRoot.EnsureEndsWith('/') + uniqueFileName |
|||
}); |
|||
} |
|||
|
|||
private static void ThrowValidationException(string message, string memberName) |
|||
{ |
|||
throw new AbpValidationException(message, |
|||
new List<ValidationResult> |
|||
{ |
|||
new ValidationResult(message, new[] {memberName}) |
|||
}); |
|||
} |
|||
|
|||
protected virtual string GenerateUniqueFileName(string extension, string prefix = null, string postfix = null) |
|||
{ |
|||
return prefix + GuidGenerator.Create().ToString("N") + postfix + extension; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System.Collections.Generic; |
|||
using System.Collections.ObjectModel; |
|||
using System.Drawing.Imaging; |
|||
using System.Linq; |
|||
|
|||
namespace Volo.Blogging.Files |
|||
{ |
|||
public class FileUploadConsts |
|||
{ |
|||
public static readonly ICollection<ImageFormat> AllowedImageUploadFormats = new Collection<ImageFormat> |
|||
{ |
|||
ImageFormat.Jpeg, |
|||
ImageFormat.Png, |
|||
ImageFormat.Gif, |
|||
ImageFormat.Bmp |
|||
}; |
|||
|
|||
public static string AllowedImageFormatsJoint => string.Join(",", AllowedImageUploadFormats.Select(x => x.ToString())); |
|||
} |
|||
} |
|||
@ -1,31 +1,55 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Blogging.Areas.Blog.Models; |
|||
using Volo.Blogging.Files; |
|||
using Volo.Blogging.Hosting; |
|||
|
|||
namespace Volo.Blogging.Areas.Blog.Controllers |
|||
{ |
|||
//TODO: This may be moved to HttpApi project since it may be needed by a SPA too.
|
|||
[Area("Blog")] |
|||
[Route("Blog/[controller]/[action]")]
|
|||
public class FilesController : AbpController |
|||
{ |
|||
private readonly IFileService _fileService; |
|||
private readonly IFileAppService _fileAppService; |
|||
|
|||
public FilesController(IFileService fileService) |
|||
public FilesController(IFileAppService fileAppService) |
|||
{ |
|||
_fileService = fileService; |
|||
_fileAppService = fileAppService; |
|||
} |
|||
|
|||
[HttpPost] |
|||
public async Task<JsonResult> UploadImage(IFormFile file) |
|||
{ |
|||
file.ValidateImage(out var fileBytes); |
|||
//TODO: localize exception messages
|
|||
|
|||
var fileUrl = await _fileService.SaveFileAsync(fileBytes, file.FileName); |
|||
if (file == null) |
|||
{ |
|||
throw new UserFriendlyException("No file found!"); |
|||
} |
|||
|
|||
return Json(new FileUploadResult(fileUrl)); |
|||
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.UploadAsync( |
|||
new FileUploadInputDto |
|||
{ |
|||
Bytes = file.AsBytes(), |
|||
Name = file.FileName |
|||
} |
|||
); |
|||
|
|||
return Json(new FileUploadResult(output.Url)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.ObjectModel; |
|||
using System.Drawing.Imaging; |
|||
using System.Linq; |
|||
|
|||
namespace Volo.Blogging |
|||
{ |
|||
public class BloggingWebConsts |
|||
{ |
|||
public class FileUploading |
|||
{ |
|||
public const string DefaultFileUploadFolderName = "files"; |
|||
|
|||
public static readonly ICollection<ImageFormat> AllowedImageUploadFormats = new Collection<ImageFormat> |
|||
{ |
|||
ImageFormat.Jpeg, |
|||
ImageFormat.Png, |
|||
ImageFormat.Gif, |
|||
ImageFormat.Bmp |
|||
}; |
|||
|
|||
public static string AllowedImageFormatsJoint => string.Join(",", AllowedImageUploadFormats.Select(x => x.ToString())); |
|||
|
|||
public const int MaxFileSize = 5242880; //5MB
|
|||
|
|||
public static int MaxFileSizeAsMegabytes => Convert.ToInt32((MaxFileSize / 1024f) / 1024f); |
|||
} |
|||
} |
|||
} |
|||
@ -1,72 +0,0 @@ |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Guids; |
|||
|
|||
namespace Volo.Blogging.Hosting |
|||
{ |
|||
public class FileService : IFileService, ITransientDependency |
|||
{ |
|||
private readonly IHostingEnvironment _hostingEnvironment; |
|||
private readonly IGuidGenerator _guidGenerator; |
|||
|
|||
public FileService(IHostingEnvironment hostingEnvironment, IGuidGenerator guidGenerator) |
|||
{ |
|||
_hostingEnvironment = hostingEnvironment; |
|||
_guidGenerator = guidGenerator; |
|||
} |
|||
|
|||
public string FileUploadDirectory |
|||
{ |
|||
get |
|||
{ |
|||
var uploadDirectory = Path.Combine(_hostingEnvironment.WebRootPath, BloggingWebConsts.FileUploading.DefaultFileUploadFolderName); |
|||
if (!Directory.Exists(uploadDirectory)) |
|||
{ |
|||
Directory.CreateDirectory(uploadDirectory); |
|||
} |
|||
|
|||
return uploadDirectory; |
|||
} |
|||
} |
|||
|
|||
public string GenerateUniqueFileName(string extension, string prefix = null, string postfix = null) |
|||
{ |
|||
return prefix + _guidGenerator.Create().ToString("N") + postfix + extension; |
|||
} |
|||
|
|||
public async Task<string> SaveFormFileAndGetUrlAsync(IFormFile file) |
|||
{ |
|||
var uniqueFileName = await SaveFileInternalAsync(file.FileName, file.AsBytes()); |
|||
return GetFileUrl(uniqueFileName); |
|||
} |
|||
|
|||
public async Task<string> SaveFileAsync(byte[] fileBytes, string originalFileName) |
|||
{ |
|||
if (fileBytes == null || fileBytes.Length == 0) |
|||
{ |
|||
throw new UserFriendlyException("File is empty!"); |
|||
} |
|||
|
|||
var uniqueFileName = await SaveFileInternalAsync(originalFileName, fileBytes); |
|||
return GetFileUrl(uniqueFileName); |
|||
} |
|||
|
|||
private static string GetFileUrl(string uniqueFileName) |
|||
{ |
|||
return "/" + BloggingWebConsts.FileUploading.DefaultFileUploadFolderName + "/" + uniqueFileName; |
|||
} |
|||
|
|||
private async Task<string> SaveFileInternalAsync(string originalFileName, byte[] fileBytes) |
|||
{ |
|||
var uniqueFileName = GenerateUniqueFileName(Path.GetExtension(originalFileName)); |
|||
var filePath = Path.Combine(FileUploadDirectory, uniqueFileName); |
|||
File.WriteAllBytes(filePath, fileBytes); //TODO: Previously was using WriteAllBytesAsync, but it's only in .netcore.
|
|||
return uniqueFileName; |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -1,16 +0,0 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Http; |
|||
|
|||
namespace Volo.Blogging.Hosting |
|||
{ |
|||
public interface IFileService |
|||
{ |
|||
string FileUploadDirectory { get; } |
|||
|
|||
string GenerateUniqueFileName(string extension, string prefix = null, string postfix = null); |
|||
|
|||
Task<string> SaveFormFileAndGetUrlAsync(IFormFile file); |
|||
|
|||
Task<string> SaveFileAsync(byte[] fileBytes, string originalFileName); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue