Skip to content

Commit

Permalink
Resolved #810: Blog Module's FilesController should support N-tier de…
Browse files Browse the repository at this point in the history
…ployment.
  • Loading branch information
hikalkan committed Feb 21, 2019
1 parent 129cd2d commit 50dd44d
Show file tree
Hide file tree
Showing 17 changed files with 185 additions and 167 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
using Volo.Abp.UI;
using Volo.Abp.VirtualFileSystem;
using Volo.Blogging;
using Volo.Blogging.Files;
using Volo.BloggingTestApp.EntityFrameworkCore;
using Volo.BloggingTestApp.MongoDb;

Expand Down Expand Up @@ -109,6 +110,12 @@ public override void ConfigureServices(ServiceConfigurationContext context)
{
options.DefaultThemeName = BasicTheme.Name;
});

Configure<BlogFileOptions>(options =>
{
options.FileUploadLocalFolder = Path.Combine(hostingEnvironment.WebRootPath, "files");
options.FileUploadUrlRoot = "/files/";
});
}

public override void OnApplicationInitialization(ApplicationInitializationContext context)
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Volo.Blogging.Files
{
public class FileUploadOutputDto
{
public string Url { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="4.5.0" />
<ProjectReference Include="..\Volo.Blogging.Application.Contracts\Volo.Blogging.Application.Contracts.csproj" />
<ProjectReference Include="..\Volo.Blogging.Domain\Volo.Blogging.Domain.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Authorization.Infrastructure;

namespace Volo.Blogging
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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; }
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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()));
}
}
Original file line number Diff line number Diff line change
@@ -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));
}
}
}
30 changes: 0 additions & 30 deletions modules/blogging/src/Volo.Blogging.Web/BloggingWebConsts.cs

This file was deleted.

72 changes: 0 additions & 72 deletions modules/blogging/src/Volo.Blogging.Web/Hosting/FileService.cs

This file was deleted.

Loading

0 comments on commit 50dd44d

Please sign in to comment.