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 =>
{