diff --git a/Functions/ImageResizerFunction.cs b/Functions/ImageResizerFunction.cs index 26ba1ee..e1cc035 100644 --- a/Functions/ImageResizerFunction.cs +++ b/Functions/ImageResizerFunction.cs @@ -31,73 +31,87 @@ public ImageProxyFunction(IImageResizerService imageProxyService, } - /* + /* * Main entry point...takes a wildcard. */ - [FunctionName("ResizeImage")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "ResizeImage/{*restOfPath}")] HttpRequest req, string restOfPath) + [FunctionName("ResizeImage")] + public async Task Run1([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "ResizeImage/{*restOfPath}")] HttpRequest req, string restOfPath) + { + return await ResizeImage(req, restOfPath, false); + } + + /* + * Main entry point...takes a wildcard. + */ + [FunctionName("ResizeImageCache")] + public async Task Run2([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "ResizeImageCache/{*restOfPath}")] HttpRequest req, string restOfPath) + { + return await ResizeImage(req, restOfPath, true); + } + + private async Task ResizeImage(HttpRequest req, string restOfPath, bool cache) { - // check to see if we have a cached version and just leave if we do - if (req.HttpContext.Request.GetTypedHeaders().IfModifiedSince.HasValue) - { - return new StatusCodeResult((int)HttpStatusCode.NotModified); - } - - try - { - // get the url - var url = restOfPath.Replace(config.GetValue("AzureContainer"), ""); - - // we need at least the url - if (string.IsNullOrEmpty(url)) - return new BadRequestObjectResult("URL is required"); - - // figure out the needed variables - // var url = req.Query["url"].ToString(); - var size = req.Query.ContainsKey("size") ? req.Query["size"].ToString() : ""; - var width = req.Query.ContainsKey("w") ? req.Query["w"].ToString().ToInt() : 0; - var height = req.Query.ContainsKey("h") ? req.Query["h"].ToString().ToInt() : 0; - var output = req.Query.ContainsKey("output") ? req.Query["output"].ToString().Replace(".", "") : url.ToSuffix(); - var mode = req.Query.ContainsKey("mode") ? req.Query["mode"].ToString() : ""; - var validOutputs = new List() { "jpg", "gif", "png", "webp" }; - - // validate the output - if (!validOutputs.Contains(output)) - output = url.ToSuffix(); - - // figure out the actual size - if (string.IsNullOrEmpty(size)) - size = $"{width}x{height}"; - - // try to resize the image - var imageStream = await this.imageResizerService.ResizeAsync(url, size, output, mode); - - if (imageStream == null) - return new NotFoundResult(); - - // choose the correct mime type - var mimeType = output switch - { - "jpg" => "image/jpeg", - "gif" => "image/gif", - "png" => "image/png", - "webp" => "image/webp", - _ => "image/jpeg" - }; - - // set cache - this.SetCacheHeaders(req.HttpContext.Response.GetTypedHeaders()); - - // return the stream - return new FileStreamResult(imageStream, mimeType); - } - catch(Exception ex) - { - return new BadRequestResult(); - } - } + // check to see if we have a cached version and just leave if we do + if (req.HttpContext.Request.GetTypedHeaders().IfModifiedSince.HasValue) + { + return new StatusCodeResult((int)HttpStatusCode.NotModified); + } + + try + { + // get the url + var url = restOfPath.Replace(config.GetValue("AzureContainer"), ""); + + // we need at least the url + if (string.IsNullOrEmpty(url)) + return new BadRequestObjectResult("URL is required"); + + // figure out the needed variables + // var url = req.Query["url"].ToString(); + var size = req.Query.ContainsKey("size") ? req.Query["size"].ToString() : ""; + var width = req.Query.ContainsKey("w") ? req.Query["w"].ToString().ToInt() : 0; + var height = req.Query.ContainsKey("h") ? req.Query["h"].ToString().ToInt() : 0; + var output = req.Query.ContainsKey("output") ? req.Query["output"].ToString().Replace(".", "") : url.ToSuffix(); + var mode = req.Query.ContainsKey("mode") ? req.Query["mode"].ToString() : ""; + var validOutputs = new List() { "jpg", "gif", "png", "webp" }; + + // validate the output + if (!validOutputs.Contains(output)) + output = url.ToSuffix(); + + // figure out the actual size + if (string.IsNullOrEmpty(size)) + size = $"{width}x{height}"; + + // try to resize the image + var imageStream = await this.imageResizerService.ResizeAsync(url, size, output, mode, cache); + + if (imageStream == null) + return new NotFoundResult(); + + // choose the correct mime type + var mimeType = output switch + { + "jpg" => "image/jpeg", + "gif" => "image/gif", + "png" => "image/png", + "webp" => "image/webp", + _ => "image/jpeg" + }; + + // set cache + //this.SetCacheHeaders(req.HttpContext.Response.GetTypedHeaders()); + + // return the stream + return new FileStreamResult(imageStream, mimeType); + } + catch (Exception ex) + { + return new BadRequestResult(); + } + } - private void SetCacheHeaders(ResponseHeaders responseHeaders) + private void SetCacheHeaders(ResponseHeaders responseHeaders) { responseHeaders.CacheControl = new CacheControlHeaderValue { Public = true }; responseHeaders.LastModified = new DateTimeOffset(new DateTime(1900, 1, 1)); diff --git a/ImageResizeProxy.csproj b/ImageResizeProxy.csproj index bc14594..4cec5be 100644 --- a/ImageResizeProxy.csproj +++ b/ImageResizeProxy.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 - v3 + net6 + v4 8.0 enable RainstormTech.Storm.ImageProxy diff --git a/Services/IImageResizerService.cs b/Services/IImageResizerService.cs index 64a5a50..becbb2d 100644 --- a/Services/IImageResizerService.cs +++ b/Services/IImageResizerService.cs @@ -6,6 +6,6 @@ namespace RainstormTech.Storm.ImageProxy { public interface IImageResizerService { - Task ResizeAsync(string url, string size, string output, string mode); + Task ResizeAsync(string url, string size, string output, string mode, bool cache = false); } } diff --git a/Services/ImageResizerService.cs b/Services/ImageResizerService.cs index 150910b..d530171 100644 --- a/Services/ImageResizerService.cs +++ b/Services/ImageResizerService.cs @@ -39,9 +39,9 @@ public ImageResizerService(HttpClient httpClient, /// /// /// - public async Task ResizeAsync(string url, string size, string output, string mode) + public async Task ResizeAsync(string url, string size, string output, string mode, bool cache = false) { - return await this.GetResultStreamAsync(url, StringToImageSize(size), output, mode); + return await this.GetResultStreamAsync(url, StringToImageSize(size), output, mode, cache); } @@ -53,7 +53,7 @@ public async Task ResizeAsync(string url, string size, string output, st /// /// /// - private async Task GetResultStreamAsync(string uri, ImageSize imageSize, string output, string mode) + private async Task GetResultStreamAsync(string uri, ImageSize imageSize, string output, string mode, bool cache = false) { // Create a BlobServiceClient object which will be used to create a container client BlobServiceClient blobServiceClient = new BlobServiceClient(config.GetConnectionString("AzureStorage")); @@ -61,7 +61,7 @@ private async Task GetResultStreamAsync(string uri, ImageSize imageSize, // get the container name string containerName = config.GetValue("AzureContainer", "storm"); - try + try { // Create the container and return a container client object var container = blobServiceClient.GetBlobContainerClient(containerName); @@ -70,14 +70,32 @@ private async Task GetResultStreamAsync(string uri, ImageSize imageSize, // Get a reference to a blob BlobClient blobClient = container.GetBlobClient(uri); + // Get cache image + var cacheUri = $"Cache{uri}_{imageSize.Width}_{imageSize.Height}_{output}_{mode}"; + if (cache) + { + var cacheBlobClient = container.GetBlobClient(cacheUri); + if (await cacheBlobClient.ExistsAsync()) + { + return (await cacheBlobClient.DownloadStreamingAsync()).Value.Content; + } + } + // Download the blob's contents and save it to a strea using (var imageStream = new MemoryStream()) { await blobClient.DownloadToAsync(imageStream); imageStream.Position = 0; - return GetResizedImage(imageStream, imageSize, output, mode); - } + var newImage = GetResizedImage(imageStream, imageSize, output, mode); + if (cache) + { + // Upload cache image + await container.UploadBlobAsync(cacheUri, newImage); + newImage.Position = 0; + } + return newImage; + } } catch(Exception ex) {