Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Martin Rud 260 posts 1001 karma points c-trib
    Sep 12, 2024 @ 07:37
    Martin Rud
    0

    How to overwrite a media file with a locale, physical file so it works both with blob and non-blob storage

    I can't get Cogworks.Tinifier package working on Umbraco v13 (and have got no solutions here: https://www.facebook.com/groups/umbracowebdevs/posts/2091914831210397/). Therefore I have tried to make my own solution (see code below).

    It works a part of the way: When user uploads an image TinifyAPI optimizes it and saves the optimized image in a temporary location (like C:\Users\marti\AppData\Local\Temp\tmpsxjjxj.tmp).

    But the part of the code that should overwrite the uploaded, non-optimized image (which is my goal - I don't want the original non-optimized image since it consumes server disc space) does not work. I.e. this part:

    using (var fileStream = System.IO.File.OpenRead(tempFilePath))
    {
        byte[] fileBytesArray;
        using (var memoryStream = new MemoryStream())
        {
            await fileStream.CopyToAsync(memoryStream);
            fileBytesArray = memoryStream.ToArray();
        }
        var fileBytes = new ByteArrayContent(fileBytesArray);
        var uploadResponse = await httpClient.PutAsync(mediaUrl, fileBytes);
        if (!uploadResponse.IsSuccessStatusCode)
        {
            NotifyUser("Upload Error", $"Failed to upload optimized image.");
        }
    }
    

    No error, but also no file is overwritten.

    My full code:

    using Umbraco.Cms.Core.Events;
    using Umbraco.Cms.Core.Notifications;
    using Umbraco.Cms.Core.Services;
    using TinifyAPI;
    using Newtonsoft.Json.Linq;
    
    namespace MartinRud.Notifications
    {
        public class MediaNotificationHandler : INotificationHandler<MediaSavedNotification>
        {
            private readonly IMediaService _mediaService;
            private readonly IConfiguration _configuration; 
            private readonly ILogger<MediaNotificationHandler> _logger;
            private readonly IEventMessagesFactory _eventMessagesFactory;
            private readonly IHttpClientFactory _httpClientFactory;
            private readonly string _baseUrl;
    
            public MediaNotificationHandler(IMediaService mediaService, IConfiguration configuration, ILogger<MediaNotificationHandler> logger, IEventMessagesFactory eventMessagesFactory, IHttpClientFactory httpClientFactory)
            {
                _mediaService = mediaService;
                _configuration = configuration;
                _logger = logger;
                _eventMessagesFactory = eventMessagesFactory;
                _httpClientFactory = httpClientFactory;
                _baseUrl = configuration["MediaBaseUrl"] ?? "https://localhost:44378";
                Tinify.Key = _configuration["TinifierSettings:ApiKey"];
            }
    
    public async void Handle(MediaSavedNotification notification)
    {
        var httpClient = _httpClientFactory.CreateClient();
    
        foreach (var mediaItem in notification.SavedEntities)
        {
            if (mediaItem.ContentType.Alias.Equals("Image"))
            {
                var mediaJson = mediaItem.GetValue<string>("umbracoFile");
                var mediaUrl = ExtractUrlFromJson(mediaJson);
    
                if (!string.IsNullOrEmpty(mediaUrl))
                {
                    if (!Uri.IsWellFormedUriString(mediaUrl, UriKind.Absolute))
                    {
                        mediaUrl = new Uri(new Uri(_baseUrl), mediaUrl).ToString();
                    }
    
                    _logger.LogInformation($"Attempting to fetch URL: {mediaUrl}");
    
                    var tempFilePath = Path.GetTempFileName();
                    var jsonFilePath = Path.ChangeExtension(tempFilePath, "json");
    
                    try
                    {
                        var response = await GetWithRetryAsync(httpClient, mediaUrl);
    
                        if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
                        {
                            NotifyUser("URL Not Found", $"The URL '{mediaUrl}' was not found.");
                            continue;
                        }
    
                        using (var fileStream = System.IO.File.Create(tempFilePath))
                        {
                            await response.Content.CopyToAsync(fileStream);
                        }
    
                        if (System.IO.File.Exists(jsonFilePath))
                        {
                            NotifyUser("Image already compressed", "The image has already been optimized.");
                            continue;
                        }
    
                        NotifyUser("Starting image optimization", $"Starting optimization for image '{mediaItem.Name}'");
                        _logger.LogInformation("Calling CompressImage for: {TempFilePath}", tempFilePath);
    
                        await CompressImage(tempFilePath, jsonFilePath);
    
                        _logger.LogInformation("Finished calling CompressImage for: {TempFilePath}", tempFilePath);
    
                        // Upload the optimized image
                        using (var fileStream = System.IO.File.OpenRead(tempFilePath))
                        {
                            byte[] fileBytesArray;
                            using (var memoryStream = new MemoryStream())
                            {
                                await fileStream.CopyToAsync(memoryStream);
                                fileBytesArray = memoryStream.ToArray();
                            }
                            var fileBytes = new ByteArrayContent(fileBytesArray);
                            var uploadResponse = await httpClient.PutAsync(mediaUrl, fileBytes);
                            if (!uploadResponse.IsSuccessStatusCode)
                            {
                                NotifyUser("Upload Error", $"Failed to upload optimized image.");
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error processing media item for: {MediaItemName}", mediaItem.Name);
                        NotifyUser("Error", $"Error processing media item: {ex.Message}");
                    }
                    finally
                    {
                        // Clean up temporary files
                        try
                        {
                            if (System.IO.File.Exists(tempFilePath))
                            {
                                //System.IO.File.Delete(tempFilePath);
                                _logger.LogInformation("Deleted temporary file: {TempFilePath}", tempFilePath);
                            }
                            if (System.IO.File.Exists(jsonFilePath))
                            {
                                //System.IO.File.Delete(jsonFilePath);
                                _logger.LogInformation("Deleted JSON file: {JsonFilePath}", jsonFilePath);
                            }
                        }
                        catch (Exception ex)
                        {
                            _logger.LogError(ex, "Error deleting temporary files.");
                        }
                    }
                }
            }
        }
    }
    
    
    private string ExtractUrlFromJson(string json)
    {
        try
        {
            var jsonObject = JObject.Parse(json);
            var url = jsonObject["src"]?.ToString();
            return url ?? string.Empty;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error parsing JSON for media URL");
            return string.Empty;
        }
    }
    
    private async Task<HttpResponseMessage> GetWithRetryAsync(HttpClient httpClient, string url, int maxRetries = 3)
    {
        for (int retry = 0; retry < maxRetries; retry++)
        {
            try
            {
                var response = await httpClient.GetAsync(url);
                response.EnsureSuccessStatusCode(); // Throws if not success code.
                return response;
            }
            catch (HttpRequestException ex)
            {
                if (retry == maxRetries - 1) // Last retry
                {
                    _logger.LogError(ex, "Error fetching URL: {Url}", url);
                    throw;
                }
                await Task.Delay(1000); // Wait before retrying
            }
        }
        throw new Exception("Failed to fetch URL after multiple retries.");
    }
            private async Task CompressImage(string filePath, string jsonFilePath)
            {
                try
                {
                    var originalSize = new FileInfo(filePath).Length;
                    var source = Tinify.FromFile(filePath);
                    await source.ToFile(filePath);
    
                    var compressedSize = new FileInfo(filePath).Length;
                    var percentageReduction = (double)(originalSize - compressedSize) / originalSize * 100;
    
                    var compressionInfo = new
                    {
                        OriginalSize = originalSize,
                        CompressedSize = compressedSize,
                        PercentageReduction = Math.Round(percentageReduction, 2)
                    };
    
                    var json = JObject.FromObject(compressionInfo);
                    await System.IO.File.WriteAllTextAsync(jsonFilePath, json.ToString());
    
                    NotifyUser("Image optimization complete", $"Optimization for image completed successfully.");
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error compressing image");
                    NotifyUser("Image optimization error", $"Error optimizing image: {ex.Message}");
                }
            }
    
            private void NotifyUser(string title, string message)
            {
                var eventMessages = _eventMessagesFactory.Get();
                eventMessages.Add(new EventMessage(title, message, EventMessageType.Success)); // or .Error if needed
            }
        }
    }
    
Please Sign in or register to post replies

Write your reply to:

Draft