using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using RMuseum.Models.Artifact; using RMuseum.Models.Auth.Memory; using RMuseum.Services; using RSecurityBackend.Models.Auth.Memory; using RSecurityBackend.Models.Generic; using RSecurityBackend.Models.Image; using System; using System.IO; using System.Net; using System.Threading.Tasks; namespace RMuseum.Controllers { [Produces("application/json")] [Route("api/images")] public class ImageController : Controller { /// /// returns image stream with image/jpeg MIME type /// /// /// norm/orig/thumb /// /// [HttpGet("{size}/{id}.jpg")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(FileStreamResult))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task Get(Guid id, string size, string mimeForResized = "image/jpeg") { RServiceResult img = await _pictureFileService.GetImage(id); if (!string.IsNullOrEmpty(img.ExceptionString)) return BadRequest(img.ExceptionString); if (img.Result == null) return NotFound(); if (!string.IsNullOrEmpty(img.Result.ExternalNormalSizeImageUrl)) { if(size != "norm") { img.Result.ExternalNormalSizeImageUrl = img.Result.ExternalNormalSizeImageUrl.Replace("/norm/", $"/{size}/"); } return Redirect(img.Result.ExternalNormalSizeImageUrl); } DateTime lastModified = img.Result.LastModified; lastModified = new DateTime(lastModified.Year, lastModified.Month, lastModified.Day, lastModified.Hour, lastModified.Minute, lastModified.Second, lastModified.Kind); Response.GetTypedHeaders().LastModified = lastModified; Response.Headers.CacheControl = "public,max-age=86400"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= lastModified) { return StatusCode(StatusCodes.Status304NotModified); } RServiceResult imgPath = _pictureFileService.GetImagePath(img.Result, size); if(!string.IsNullOrEmpty(imgPath.ExceptionString)) return BadRequest(imgPath.ExceptionString); if(string.IsNullOrEmpty(imgPath.Result)) { if(size == "orig") { imgPath = _pictureFileService.GetImagePath(img.Result, "norm"); if (!string.IsNullOrEmpty(imgPath.ExceptionString)) return BadRequest(imgPath.ExceptionString); } else { return NotFound(); } } return new FileStreamResult(new FileStream(imgPath.Result, FileMode.Open, FileAccess.Read), size == "orig" ? img.Result.ContentType : mimeForResized ); } /// /// returns image stream with image/webp MIME type /// /// /// /// [HttpGet("{size}/{id}.webp")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(FileStreamResult))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetWebp(Guid id, string size) { return await Get(id, size, "image/webp"); } /// /// Rotate Image in 90 deg. multiplicants: 90, 180 or 270 /// /// /// /// [HttpPut("{id}/{degIn90mul}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RPictureFile))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task Rotate(Guid id, int degIn90mul) { RServiceResult res = await _pictureFileService.RotateImage(id, degIn90mul); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// replace an image /// /// /// /// [HttpPut("replace/{id}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RPictureFile))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task ReplaceImageAsync(Guid id, IFormFile file) { RServiceResult res = await _pictureFileService.ReplaceImageAsync(id, file); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// Generate Cropped Image Based On ThumbnailCoordinates For Notes /// /// /// /// /// /// /// id of cropped image [HttpGet("cropped/{id}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RImage))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GenerateCroppedImageBasedOnThumbnailCoordinates(Guid id, decimal left, decimal top, decimal width, decimal height) { RServiceResult cropped = await _pictureFileService.GenerateCroppedImageBasedOnThumbnailCoordinates(id, left, top, width, height); if (!string.IsNullOrEmpty(cropped.ExceptionString)) return BadRequest(cropped.ExceptionString); return Ok(cropped.Result); } /// /// constructor /// /// /// public ImageController(IPictureFileService pictureFileService) { _pictureFileService = pictureFileService; } /// /// Artifact Service /// protected readonly IPictureFileService _pictureFileService; } }