diff --git a/src/BootstrapBlazor.Server/Components/Samples/UploadAvatars.razor b/src/BootstrapBlazor.Server/Components/Samples/UploadAvatars.razor index c18a81a9598..d53e429e239 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/UploadAvatars.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/UploadAvatars.razor @@ -55,13 +55,13 @@ - + - +
diff --git a/src/BootstrapBlazor.Server/Components/Samples/UploadAvatars.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/UploadAvatars.razor.cs index bdef10c2e00..d3d4b8b0e37 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/UploadAvatars.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/UploadAvatars.razor.cs @@ -37,39 +37,14 @@ protected override void OnInitialized() ]); } - private async Task OnChange(UploadFile file) + private Task OnAvatarValidSubmit(EditContext context) { - // 示例代码,使用 base64 格式 - if (file is { File: not null }) - { - var format = file.File.ContentType; - if (file.IsImage()) - { - _token ??= new CancellationTokenSource(); - if (_token.IsCancellationRequested) - { - _token.Dispose(); - _token = new CancellationTokenSource(); - } - - await file.RequestBase64ImageFileAsync(format, 640, 480, MaxFileLength, null, _token.Token); - } - else - { - file.Code = 1; - file.Error = Localizer["UploadsFormatError"]; - } - - if (file.Code != 0) - { - await ToastService.Error(Localizer["UploadsAvatarMsg"], $"{file.Error} {format}"); - } - } + return ToastService.Success(Localizer["UploadsValidateFormTitle"], Localizer["UploadsValidateFormValidContent"]); } - private Task OnAvatarValidSubmit(EditContext context) + private Task OnAvatarInValidSubmit(EditContext context) { - return ToastService.Error(Localizer["UploadsValidateFormTitle"], Localizer["UploadsValidateFormValidContent"]); + return ToastService.Error(Localizer["UploadsValidateFormTitle"], Localizer["UploadsValidateFormInValidContent"]); } /// diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 65cd7bd79d6..ff88e9dae37 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -3481,6 +3481,7 @@ "UploadsBorderRadius": "Border radius", "UploadsValidateFormTitle": "ValidateForm", "UploadsValidateFormValidContent": "Saved successfully", + "UploadsValidateFormInValidContent": "Please correct it and submit the form again", "UploadsFormatError": "The file format is incorrect", "UploadsAvatarMsg": "Avatar upload" }, diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 4411ee902bb..211bd6fdbc7 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -3481,6 +3481,7 @@ "UploadsBorderRadius": "预览框圆角曲率", "UploadsValidateFormTitle": "表单应用", "UploadsValidateFormValidContent": "数据合规,保存成功", + "UploadsValidateFormInValidContent": "数据不合规,请更正后再提交表单", "UploadsFormatError": "文件格式不正确", "UploadsAvatarMsg": "头像上传" }, diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index 8b68a932561..ac8e7c9c6ae 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@  - 9.7.1-beta04 + 9.7.1-beta05 diff --git a/src/BootstrapBlazor/Components/Upload/AvatarUpload.razor.cs b/src/BootstrapBlazor/Components/Upload/AvatarUpload.razor.cs index 6c45f79c416..7aca5d0fa1f 100644 --- a/src/BootstrapBlazor/Components/Upload/AvatarUpload.razor.cs +++ b/src/BootstrapBlazor/Components/Upload/AvatarUpload.razor.cs @@ -77,6 +77,12 @@ public partial class AvatarUpload [Parameter] public bool IsUploadButtonAtFirst { get; set; } + /// + /// 获得/设置 是否允许预览回调方法 默认 null + /// + [Parameter] + public Func? CanPreviewCallback { get; set; } + [Inject] [NotNull] private IIconTheme? IconTheme { get; set; } @@ -133,14 +139,12 @@ protected override void OnParametersSet() /// protected override async Task TriggerOnChanged(UploadFile file) { - if (OnChange == null) - { - await file.RequestBase64ImageFileAsync(allowExtensions: AllowExtensions); - } - else + // 从客户端获得预览地址不使用 base64 编码 + if (file.IsImage(AllowExtensions, CanPreviewCallback)) { - await OnChange(file); + file.PrevUrl = await InvokeAsync("getPreviewUrl", Id, file.OriginFileName); } + await base.TriggerOnChanged(file); } private IReadOnlyCollection _results = []; diff --git a/src/BootstrapBlazor/Components/Upload/CardUpload.razor.cs b/src/BootstrapBlazor/Components/Upload/CardUpload.razor.cs index 989082efb76..c5363e0c09b 100644 --- a/src/BootstrapBlazor/Components/Upload/CardUpload.razor.cs +++ b/src/BootstrapBlazor/Components/Upload/CardUpload.razor.cs @@ -133,6 +133,21 @@ protected override void OnParametersSet() RemoveIcon ??= IconTheme.GetIconByKey(ComponentIcons.CardUploadRemoveIcon); } + /// + /// + /// + /// + /// + protected override async Task TriggerOnChanged(UploadFile file) + { + // 从客户端获得预览地址不使用 base64 编码 + if (file.IsImage(AllowExtensions, CanPreviewCallback)) + { + file.PrevUrl = await InvokeAsync("getPreviewUrl", Id, file.OriginFileName); + } + await base.TriggerOnChanged(file); + } + private async Task OnCardFileDelete(UploadFile item) { await OnFileDelete(item); diff --git a/src/BootstrapBlazor/Extensions/UploadFileExtensions.cs b/src/BootstrapBlazor/Extensions/UploadFileExtensions.cs index 9897efed35d..94f4eab7f4f 100644 --- a/src/BootstrapBlazor/Extensions/UploadFileExtensions.cs +++ b/src/BootstrapBlazor/Extensions/UploadFileExtensions.cs @@ -194,14 +194,14 @@ public static async Task SaveToFileAsync(this UploadFile upload, string fi /// /// /// - /// + /// /// - public static bool IsImage(this UploadFile item, List? allowExtensions = null, Func? _callback = null) + public static bool IsImage(this UploadFile item, List? allowExtensions = null, Func? callback = null) { bool ret; - if (_callback != null) + if (callback != null) { - ret = _callback(item); + ret = callback(item); } else if (item.File != null) { diff --git a/src/BootstrapBlazor/wwwroot/modules/upload.js b/src/BootstrapBlazor/wwwroot/modules/upload.js index 28334a53267..b646000c56d 100644 --- a/src/BootstrapBlazor/wwwroot/modules/upload.js +++ b/src/BootstrapBlazor/wwwroot/modules/upload.js @@ -1,20 +1,24 @@ import Data from "./data.js" import EventHandler from "./event-handler.js" +import { readFileAsync } from "./utility.js" export function init(id) { const el = document.getElementById(id) if (el === null) { return } - const preventHandler = e => e.preventDefault() + const preventHandler = e => e.preventDefault(); const body = el.querySelector('.upload-drop-body'); - const upload = { el, body, preventHandler } + const inputFile = el.querySelector('[type="file"]'); + const upload = { el, body, preventHandler, inputFile }; Data.set(id, upload) - const inputFile = el.querySelector('[type="file"]') EventHandler.on(el, 'click', '.btn-browser', () => { inputFile.click() }) + EventHandler.on(inputFile, 'change', e => { + upload.files = e.delegateTarget.files; + }); EventHandler.on(document, "dragleave", preventHandler) EventHandler.on(document, 'drop', preventHandler) @@ -79,12 +83,28 @@ export function init(id) { }) } +export async function getPreviewUrl(id, fileName) { + let url = ''; + const upload = Data.get(id); + const { files } = upload; + if (files) { + const file = [...files].find(v => v.name === fileName); + if (file) { + const data = await readFileAsync(file); + if (data) { + url = URL.createObjectURL(data); + } + } + } + return url; +} + export function dispose(id) { const upload = Data.get(id) Data.remove(id) if (upload) { - const { el, body, preventHandler } = upload; + const { el, body, preventHandler, inputFile } = upload; EventHandler.off(document, 'dragleave', preventHandler) EventHandler.off(document, 'drop', preventHandler) @@ -94,6 +114,7 @@ export function dispose(id) { EventHandler.off(el, 'click') EventHandler.off(el, 'drop') EventHandler.off(el, 'paste') + EventHandler.off(inputFile, 'change') EventHandler.off(body, 'dragleave') EventHandler.off(body, 'drop') EventHandler.off(body, 'dragenter') diff --git a/src/BootstrapBlazor/wwwroot/modules/utility.js b/src/BootstrapBlazor/wwwroot/modules/utility.js index b5ea69e7025..aecd390294d 100644 --- a/src/BootstrapBlazor/wwwroot/modules/utility.js +++ b/src/BootstrapBlazor/wwwroot/modules/utility.js @@ -886,6 +886,27 @@ export function drawImage(canvas, image, offsetWidth, offsetHeight) { context.drawImage(image, 0, 0, offsetWidth, offsetHeight); } +/** + * @param {File} file + * @returns {Blob} + */ +export function readFileAsync(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + + reader.onload = () => { + const blob = new Blob([reader.result], { type: file.type }); + resolve(blob); + }; + + reader.onerror = (error) => { + reject(error); + }; + + reader.readAsArrayBuffer(file); + }); +} + export { autoAdd, autoRemove, diff --git a/test/UnitTest/Components/UploadAvatarTest.cs b/test/UnitTest/Components/UploadAvatarTest.cs index 6afe95d8472..a456bae0c5e 100644 --- a/test/UnitTest/Components/UploadAvatarTest.cs +++ b/test/UnitTest/Components/UploadAvatarTest.cs @@ -17,6 +17,7 @@ public async Task AvatarUpload_Ok() UploadFile? uploadFile = null; var cut = Context.RenderComponent>(pb => { + pb.Add(a => a.CanPreviewCallback, null); pb.Add(a => a.IsMultiple, true); pb.Add(a => a.OnChange, file => {