Merge pull request #541 from sughosneo/feature/default-image-template

Removes the image upload functionality
This commit is contained in:
Nish Anil
2021-06-30 20:08:26 +05:30
committed by GitHub
11 changed files with 19 additions and 250 deletions

View File

@@ -1,10 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
{
public interface IFileSystem
{
Task<bool> SavePicture(string pictureName, string pictureBase64, CancellationToken cancellationToken);
}
}

View File

@@ -7,11 +7,11 @@ namespace Microsoft.eShopWeb.ApplicationCore.Specifications
{
public CatalogFilterPaginatedSpecification(int skip, int take, int? brandId, int? typeId)
: base()
{
{
Query
.Where(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
(!typeId.HasValue || i.CatalogTypeId == typeId))
.Paginate(skip, take);
.Skip(skip).Take(take);
}
}
}

View File

@@ -80,23 +80,7 @@
<InputNumber @bind-Value="_item.Price" class="form-control" />
<ValidationMessage For="(() => _item.Price)" />
</div>
</div>
<div class="form-group">
<label class="control-label col-md-6">@_item.PictureName</label>
<div class="row">
<div class="col-md-6 esh-form-information">
<BlazorInputFile.InputFile OnChange="AddFile"/>
</div>
<div class="col-md-6 esh-form-information">
@if (HasPicture)
{
<button type="button" class="btn btn-danger" @onclick="RemoveImage">Remove Picture</button>
}
</div>
<span class="col-md-12" style="color: red;"> @_badFileMessage </span>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -171,26 +155,4 @@
_modalClass = "";
_showCreateModal = false;
}
private async Task AddFile(IFileListEntry[] files)
{
_badFileMessage = string.Empty;
var file = files.FirstOrDefault();
_item.PictureName = file?.Name;
_item.PictureBase64 = await CatalogItem.DataToBase64(file);
_badFileMessage = CatalogItem.IsValidImage(_item.PictureName, _item.PictureBase64);
if (!string.IsNullOrEmpty(_badFileMessage))
{
_item.PictureName = null;
_item.PictureBase64 = null;
}
}
private void RemoveImage()
{
_item.PictureName = null;
_item.PictureBase64 = null;
}
}

View File

@@ -83,23 +83,7 @@
<InputNumber @bind-Value="_item.Price" class="form-control" />
<ValidationMessage For="(() => _item.Price)" />
</div>
</div>
<div class="form-group">
<label class="control-label col-md-6">@_item.PictureName</label>
<div class="row">
<div class="col-md-6 esh-form-information">
<BlazorInputFile.InputFile OnChange="ChangeFile" />
</div>
<div class="col-md-6 esh-form-information">
@if (HasPicture)
{
<button type="button" class="btn btn-danger" @onclick="RemoveImage">Remove Picture</button>
}
</div>
<span class="col-md-12" style="color: red;"> @_badFileMessage </span>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -170,27 +154,4 @@
_modalClass = "";
_showEditModal = false;
}
private async Task ChangeFile(IFileListEntry[] files)
{
_badFileMessage = string.Empty;
var file = files.FirstOrDefault();
_item.PictureName = file?.Name;
_item.PictureBase64 = await CatalogItem.DataToBase64(file);
_badFileMessage = CatalogItem.IsValidImage(_item.PictureName, _item.PictureBase64);
if (!string.IsNullOrEmpty(_badFileMessage))
{
_item.PictureName = null;
_item.PictureBase64 = null;
}
}
private void RemoveImage()
{
_item.PictureName = null;
_item.PictureBase64 = null;
_item.PictureUri = null;
}
}

View File

@@ -1,84 +0,0 @@
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Data;
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Infrastructure.Services
{
public class WebFileSystem : IFileSystem
{
private readonly HttpClient _httpClient;
private readonly string _url;
public const string AUTH_KEY = "AuthKeyOfDoomThatMustBeAMinimumNumberOfBytes";
public WebFileSystem(string url)
{
_url = url;
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Add("auth-key", AUTH_KEY);
}
public async Task<bool> SavePicture(string pictureName, string pictureBase64, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(pictureBase64) || !await UploadFile(pictureName, Convert.FromBase64String(pictureBase64), cancellationToken))
{
return false;
}
return true;
}
private async Task<bool> UploadFile(string fileName, byte[] fileData, CancellationToken cancellationToken)
{
if (!fileData.IsValidImage(fileName))
{
return false;
}
return await UploadToWeb(fileName, fileData, cancellationToken);
}
private async Task<bool> UploadToWeb(string fileName, byte[] fileData, CancellationToken cancellationToken)
{
var request = new FileItem
{
DataBase64 = Convert.ToBase64String(fileData),
FileName = fileName
};
var content = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");
using var message = await _httpClient.PostAsync(_url, content, cancellationToken);
if (!message.IsSuccessStatusCode)
{
return false;
}
return true;
}
}
public static class ImageValidators
{
private const int ImageMaximumBytes = 512000;
public static bool IsValidImage(this byte[] postedFile, string fileName)
{
return postedFile != null && postedFile.Length > 0 && postedFile.Length <= ImageMaximumBytes && IsExtensionValid(fileName);
}
private static bool IsExtensionValid(string fileName)
{
var extension = Path.GetExtension(fileName);
return string.Equals(extension, ".jpg", StringComparison.OrdinalIgnoreCase) ||
string.Equals(extension, ".png", StringComparison.OrdinalIgnoreCase) ||
string.Equals(extension, ".gif", StringComparison.OrdinalIgnoreCase) ||
string.Equals(extension, ".jpeg", StringComparison.OrdinalIgnoreCase);
}
}
}

View File

@@ -19,13 +19,11 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
{
private readonly IAsyncRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;
private readonly IFileSystem _webFileSystem;
public Create(IAsyncRepository<CatalogItem> itemRepository, IUriComposer uriComposer, IFileSystem webFileSystem)
public Create(IAsyncRepository<CatalogItem> itemRepository, IUriComposer uriComposer)
{
_itemRepository = itemRepository;
_uriComposer = uriComposer;
_webFileSystem = webFileSystem;
}
[HttpPost("api/catalog-items")]
@@ -45,12 +43,12 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
if (newItem.Id != 0)
{
var picName = $"{newItem.Id}{Path.GetExtension(request.PictureName)}";
if (await _webFileSystem.SavePicture(picName, request.PictureBase64, cancellationToken))
{
newItem.UpdatePictureUri(picName);
await _itemRepository.UpdateAsync(newItem, cancellationToken);
}
//We disabled the upload functionality and added a default/placeholder image to this sample due to a potential security risk
// pointed out by the community. More info in this issue: https://github.com/dotnet-architecture/eShopOnWeb/issues/537
// In production, we recommend uploading to a blob storage and deliver the image via CDN after a verification process.
newItem.UpdatePictureUri("eCatalog-item-default.png");
await _itemRepository.UpdateAsync(newItem, cancellationToken);
}
var dto = new CatalogItemDto

View File

@@ -17,15 +17,12 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
.WithResponse<UpdateCatalogItemResponse>
{
private readonly IAsyncRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;
private readonly IFileSystem _webFileSystem;
private readonly IUriComposer _uriComposer;
public Update(IAsyncRepository<CatalogItem> itemRepository, IUriComposer uriComposer, IFileSystem webFileSystem)
public Update(IAsyncRepository<CatalogItem> itemRepository, IUriComposer uriComposer)
{
_itemRepository = itemRepository;
_uriComposer = uriComposer;
_webFileSystem = webFileSystem;
_uriComposer = uriComposer;
}
[HttpPut("api/catalog-items")]
@@ -43,20 +40,7 @@ namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints
existingItem.UpdateDetails(request.Name, request.Description, request.Price);
existingItem.UpdateBrand(request.CatalogBrandId);
existingItem.UpdateType(request.CatalogTypeId);
if (string.IsNullOrEmpty(request.PictureBase64) && string.IsNullOrEmpty(request.PictureUri))
{
existingItem.UpdatePictureUri(string.Empty);
}
else
{
var picName = $"{existingItem.Id}{Path.GetExtension(request.PictureName)}";
if (await _webFileSystem.SavePicture($"{picName}", request.PictureBase64, cancellationToken))
{
existingItem.UpdatePictureUri(picName);
}
}
existingItem.UpdateType(request.CatalogTypeId);
await _itemRepository.UpdateAsync(existingItem, cancellationToken);

View File

@@ -95,8 +95,7 @@ namespace Microsoft.eShopWeb.PublicApi
services.AddScoped<ITokenClaimsService, IdentityTokenClaimService>();
var baseUrlConfig = new BaseUrlConfiguration();
Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig);
services.AddScoped<IFileSystem, WebFileSystem>(x => new WebFileSystem($"{baseUrlConfig.WebBase}File"));
Configuration.Bind(BaseUrlConfiguration.CONFIG_NAME, baseUrlConfig);
services.AddMemoryCache();

View File

@@ -1,38 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopWeb.Web.ViewModels.File;
using System;
using System.IO;
namespace Microsoft.eShopWeb.Web.Controllers
{
[Route("[controller]")]
[ApiController]
public class FileController : ControllerBase
{
[HttpPost]
[AllowAnonymous]
public IActionResult Upload(FileViewModel fileViewModel)
{
if (!Request.Headers.ContainsKey("auth-key") || Request.Headers["auth-key"].ToString() != ApplicationCore.Constants.AuthorizationConstants.AUTH_KEY)
{
return Unauthorized();
}
if(fileViewModel == null || string.IsNullOrEmpty(fileViewModel.DataBase64)) return BadRequest();
var fileData = Convert.FromBase64String(fileViewModel.DataBase64);
if (fileData.Length <= 0) return BadRequest();
var fullPath = Path.Combine(Directory.GetCurrentDirectory(), @"wwwroot/images/products", fileViewModel.FileName);
if (System.IO.File.Exists(fullPath))
{
System.IO.File.Delete(fullPath);
}
System.IO.File.WriteAllBytes(fullPath, fileData);
return Ok();
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -18,8 +18,7 @@ namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers
private int _testBrandId = 1;
private int _testTypeId = 2;
private string _testDescription = "test description";
private string _testName = "test name";
private string _testUri = "test uri";
private string _testName = "test name";
private decimal _testPrice = 1.23m;
public CreateEndpoint(ApiTestFixture factory)
@@ -54,8 +53,7 @@ namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers
Assert.Equal(_testBrandId, model.CatalogItem.CatalogBrandId);
Assert.Equal(_testTypeId, model.CatalogItem.CatalogTypeId);
Assert.Equal(_testDescription, model.CatalogItem.Description);
Assert.Equal(_testName, model.CatalogItem.Name);
Assert.Equal(_testUri, model.CatalogItem.PictureUri);
Assert.Equal(_testName, model.CatalogItem.Name);
Assert.Equal(_testPrice, model.CatalogItem.Price);
}
@@ -66,8 +64,7 @@ namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers
CatalogBrandId = _testBrandId,
CatalogTypeId = _testTypeId,
Description = _testDescription,
Name = _testName,
PictureUri = _testUri,
Name = _testName,
Price = _testPrice
};
var jsonContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json");