CodeBeam.MudBlazor.Extensions icon indicating copy to clipboard operation
CodeBeam.MudBlazor.Extensions copied to clipboard

New Component? Mud Blazor Image Uploader and Resize

Open enkodellc opened this issue 2 years ago • 1 comments

Blazor Image Uploader and Resize component?? I have a decent start and use it internally. This requires a bit more complexity with server side and client side.

Another blog with their take on it.

Here is my code for example reference

<ImageUploader @bind-ImageUrl="@easyButtonDto.Image3" Label="Additional Marketing Image 2" MaxWidth="1900" MaxHeight="1200" PlaceHolder="Enter in the link for your additional marketing image 1" AcceptedFileExtenstions=".jpg,.png,.svg" Required="true" />

DTO handling the file and the details for server side:

public class FileUploadDto
    {
        public byte[] FileContent { get; set; }
        public string FileType { get; set; }
        public long FileSize { get; set; }
        public bool Uploaded { get; set; }
        public string FileName { get; set; }
        public string StoredFileName { get; set; }
        public string Folder { get; set; }
        public int ErrorCode { get; set; }
        public string Link { get; set; }
        public int MaxWidth { get; set; }
        public int MaxHeight { get; set; }

        public async Task WriteToStreamAsync(IBrowserFile fileUploadEntry)
        {
            using var stream = new MemoryStream();
            var buffer = new byte[fileUploadEntry.Size];

            using var newFileStream = fileUploadEntry.OpenReadStream(104857600);

            int bytesRead;
            double totalRead = 0;

            while ((bytesRead = await newFileStream.ReadAsync(buffer)) != 0)
            {
                totalRead += bytesRead;
                await stream.WriteAsync(buffer, 0, bytesRead);
            }

            FileContent = stream.GetBuffer();
        }

Here is my Image component:

@*ImageUploader*@
@using BlazorBoilerplate.Shared.Dto.FileUpload

@inject AppState appState
@inject Microsoft.JSInterop.IJSRuntime js

@inject NavigationManager navigationManager
@inject IApiClient apiClient
@inject IViewNotifier viewNotifier

<h3>@Label</h3>           
    @if (MaxWidth > 0 && MaxHeight > 0)
    {
        <sup>Suggested / Max Dimensions: @MaxHeight x @MaxWidth pixels</sup>
    }
@if (!string.IsNullOrEmpty(@ImageUrl))
{
    <div style="padding: 10px; text-align:center; background:#f1f1f1;">
        <img src="@(ImageUrl)" style="max-height:150px"/>
    </div>
}
<br />
<MudTextField Value="@ImageUrl" Label="@(Label + " Url")" AdornmentIcon="@Icons.Material.Filled.Image" Adornment="Adornment.End" ValueChanged="ImageUrlChanged" FullWidth="true" Required="@Required" RequiredError="Required"
              Placeholder="@PlaceHolder" HelperText="@($"Enter in the url link to your {Label}")" HelperTextOnFocus="true" Clearable="true"></MudTextField>

<MudPaper @ondragenter="@(()=>_dragEnterStyle="drag-enter")" @ondragleave="@(()=>_dragEnterStyle=null)" @ondragend="@(()=>_dragEnterStyle=null)" Class=@("drag-drop-zone "+ _dragEnterStyle)>
    <InputFile OnChange="ImageFilesReady" class="drag-drop-input" accept="@AcceptedFileExtenstions" />
    Drop a single @AcceptedFileExtenstions file here or click to browse<MudIcon Icon="@Icons.Material.Filled.AttachFile"></MudIcon>
</MudPaper>

<MudOverlay @bind-Visible="isBusy" DarkBackground="true">
    <MudProgressCircular Color="Color.Secondary" Size="Size.Large" Indeterminate="true" />
</MudOverlay>

@code {
    [Parameter]
    public string ImageUrl { get; set; } = "";

    [Parameter]
    public bool Required { get; set; } = false;

    [Parameter]
    public string AcceptedFileExtenstions { get; set; } = ".png,.svg";

    [Parameter]
    public string Label { get; set; } = "Image Url / Uploader";

    [Parameter]
    public string PlaceHolder { get; set; } = "Enter in your Image URL Path or Upload";

    [Parameter]
    public int MaxWidth { get; set; }

    [Parameter]
    public int MaxHeight { get; set; }

    [Parameter]
    public EventCallback<String> ImageUrlChanged { get; set; }

    string _dragEnterStyle;

    public bool isBusy = false;

    List<string> fileStringList = new List<string>();

    public async Task ImageFilesReady(InputFileChangeEventArgs e)
    {
        await FilesReady(e);
    }

    private async Task FilesReady(InputFileChangeEventArgs e)
    {
        viewNotifier.Show("Uploading your img file",  ViewNotifierType.Info);
        isBusy = true;
        StateHasChanged();
        try
        {
            var files = e.GetMultipleFiles();
            foreach (var file in files)
            {
                fileStringList.Add($"Name: {file.Name} - Size: {file.Size}");
                List<string> extensions = AcceptedFileExtenstions.Split(",").ToList();

                bool isValid = false;
                foreach (string ext in extensions){
                    if (file?.Name.EndsWith(ext.ToLower()) == true) 
                    {
                        isValid = true;
                        break;
                    }                    
                }

                if (!isValid)
                {
                    viewNotifier.Show($"The accepted file types are: {AcceptedFileExtenstions}", ViewNotifierType.Error, "Incorrect File Type");
                    fileStringList.Clear();
                    return;
                }

                FileUploadDto fileUpload = new FileUploadDto();

                await fileUpload.WriteToStreamAsync(file);

                fileUpload.FileName = file.Name;
                fileUpload.FileSize = file.Size;
                fileUpload.FileType = file.ContentType;
                fileUpload.MaxHeight = MaxHeight;
                fileUpload.MaxWidth = MaxWidth;

                ApiResponseDto apiResponse = await apiClient.FileUpload(fileUpload);

                if (apiResponse.StatusCode == 200)
                {
                    FileUploadDto fileUploadDto = Newtonsoft.Json.JsonConvert.DeserializeObject<FileUploadDto>(apiResponse.Result.ToString());

                    if (fileUploadDto.Uploaded)
                    {
                        ImageUrl = fileUploadDto.Link;
                        await ImageUrlChanged.InvokeAsync(ImageUrl);
                        StateHasChanged();
                        return;
                    }
                }
                else
                {
                    viewNotifier.Show(apiResponse.Message + " : " + apiResponse.StatusCode, ViewNotifierType.Error, "Upload Operation Failed");
                }
            };
        }
        catch (Exception ex)
        {
            viewNotifier.Show(ex.Message, ViewNotifierType.Error, "Error");
        }
        finally
        {
            isBusy = false;
            StateHasChanged();
        }
        return;
    }

    protected override async Task OnInitializedAsync()
    {
        await OnValueChanged(ImageUrl);
        await base.OnInitializedAsync();
    }

    private async Task ClearImageValue()
    {
        ImageUrl = "";
        await OnValueChanged(null);
    }

    private async Task OnValueChanged(string imageUrl)
    {
        if (imageUrl == null)
        {
            await ImageUrlChanged.InvokeAsync(null);
            return;
        }

        ImageUrl = imageUrl;
        await ImageUrlChanged.InvokeAsync(imageUrl);
        StateHasChanged();
    }
}

Server side:

        [HttpPost]
        public async Task<ApiResponse> Upload([FromBody] FileUploadDto fileUpload)
        {
            string trustedFileNameForFileStorage;
            var untrustedFileName = fileUpload.FileName;
            var trustedFileNameForDisplay = WebUtility.HtmlEncode(untrustedFileName);
            string ext = Path.GetExtension(fileUpload.FileName);

            if (fileUpload.FileSize == 0)
            {
                fileUpload.ErrorCode = 1;
                return new ApiResponse(Status404NotFound, L["File not selected"]);
            }

            if (fileUpload.FileSize > 1024 * 1024 * 20)
            {
                fileUpload.ErrorCode = 2;
                return new ApiResponse(Status400BadRequest, L["File too Large"]);
            }

            switch (fileUpload.FileType)
            {
                case "text/csv":
                    break;
                case "image/png":
                case "image/jpg":
                case "image/jpeg":
                    try
                    {
                        trustedFileNameForFileStorage = Path.GetRandomFileName().Replace(".", "_") + Path.GetExtension(fileUpload.FileName);
                        var path = Path.Combine(_env.ContentRootPath, "cdn", trustedFileNameForFileStorage);

                        ImageConverter imageConverter = new System.Drawing.ImageConverter();
                        System.Drawing.Image image = imageConverter.ConvertFrom(fileUpload.FileContent) as System.Drawing.Image;

                        //TODO Image REsize

                        if (ext == ".jpg") image.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg);
                        if (ext == ".png") image.Save(path, System.Drawing.Imaging.ImageFormat.Png);

                        fileUpload.Uploaded = true;
                        fileUpload.Folder = "cdn";
                        fileUpload.StoredFileName = trustedFileNameForFileStorage;
                        fileUpload.Link = $"YOURPUBLICDOMAINFOLDER/{fileUpload.Folder}/{fileUpload.StoredFileName}";
                    }
                    catch (Exception ex)
                    {
                        fileUpload.ErrorCode = 3;
                        return new ApiResponse(Status400BadRequest, $"Could not Save Image: {ex.Message}");
                    }
                    break;

                case "image/svg+xml":
                    try
                    {
                        trustedFileNameForFileStorage = Path.GetRandomFileName().Replace(".", "_") + Path.GetExtension(fileUpload.FileName);
                        var path = Path.Combine(_env.ContentRootPath, "cdn", trustedFileNameForFileStorage);

                        string svgString = "";
                        using (var reader = new StreamReader(new MemoryStream(fileUpload.FileContent), Encoding.Default))
                        {
                            svgString = await reader.ReadToEndAsync();
                        }

                        int length = svgString.Length;
                        var svgEndIndex = svgString.LastIndexOf("</svg>");

                        if (svgEndIndex > 0) svgString = svgString.Substring(0, svgEndIndex + 6);

                        StreamWriter streamWriter = new StreamWriter(path);
                        streamWriter.AutoFlush = true;
                        try
                        {
                            streamWriter.Write(svgString);
                        }
                        finally
                        {
                            streamWriter.Flush();
                            streamWriter.Close();
                            streamWriter.Dispose();
                        }

                        fileUpload.Uploaded = true;
                        fileUpload.Folder = "cdn";
                        fileUpload.StoredFileName = trustedFileNameForFileStorage;
                        fileUpload.Link = $"YOURPUBLICDOMAINFOLDER/{fileUpload.Folder}/{fileUpload.StoredFileName}";
                    }
                    catch (IOException ex)
                    {
                        fileUpload.ErrorCode = 3;
                    }
                    break;

                default:
                    return new ApiResponse(Status400BadRequest, L["Unknown file type"]);
            }

            return new ApiResponse(Status200OK, null, fileUpload);
        }

enkodellc avatar Oct 23 '22 19:10 enkodellc