Skip to content

Commit 0975ebd

Browse files
j4587698ArgoZhang
andauthored
feat(Editor): add OnFileUpload parameter (#532)
* Editor增加Upload回调 * chore: bump version 9.0.2 * doc: 文档注释格式化 * feat: 增加创建图片节点方法 * refactor: 增加 ImageUpload 回调 * refactor: 增加 SaveToFileAsync 扩展方法 * refactor: 更新 EditorUploadFile 类 * perf: 优化性能减少一次 JS 调度开销 * chore: bump version 9.0.5 --------- Co-authored-by: Argo Zhang <argo@live.ca>
1 parent 9f653e4 commit 0975ebd

5 files changed

Lines changed: 156 additions & 5 deletions

File tree

src/components/BootstrapBlazor.SummerNote/BootstrapBlazor.SummerNote.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
4-
<Version>9.0.4</Version>
4+
<Version>9.0.5</Version>
55
</PropertyGroup>
66

77
<PropertyGroup>

src/components/BootstrapBlazor.SummerNote/Components/Editor/Editor.razor.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ public partial class Editor
6060
[Parameter]
6161
public bool ShowSubmit { get; set; } = true;
6262

63+
/// <summary>
64+
/// 获得/设置 Editor 组件内上传文件时回调此方法
65+
/// </summary>
66+
[Parameter]
67+
public Func<EditorUploadFile, Task>? OnFileUpload { get; set; }
68+
6369
private bool _lastShowSubmit = true;
6470

6571
private string? ShowSubmitString => ShowSubmit ? "true" : null;
@@ -83,7 +89,7 @@ public partial class Editor
8389
public string? Language { get; set; }
8490

8591
/// <summary>
86-
/// 获得/设置 语言扩展脚本路径 默认 null 如加载德语可设置为
92+
/// 获得/设置 语言扩展脚本路径 默认 null 如加载德语可设置为
8793
/// </summary>
8894
[Parameter]
8995
public string? LanguageUrl { get; set; }
@@ -186,7 +192,7 @@ protected override async Task InvokeInitAsync()
186192
methodClickPluginItem = nameof(ClickPluginItem);
187193
}
188194

189-
await InvokeVoidAsync("init", Id, Interop, methodGetPluginAttributes, methodClickPluginItem, Height, Value ?? "", Language, LanguageUrl);
195+
await InvokeVoidAsync("init", Id, Interop, methodGetPluginAttributes, methodClickPluginItem, Height, Value ?? "", Language, LanguageUrl, OnFileUpload is not null);
190196
}
191197

192198
/// <summary>
@@ -256,6 +262,24 @@ public async Task<string> ClickPluginItem(string pluginItemName)
256262
return ret;
257263
}
258264

265+
/// <summary>
266+
/// 文件上传回调
267+
/// </summary>
268+
/// <param name="name"></param>
269+
/// <param name="contentType"></param>
270+
/// <param name="size"></param>
271+
/// <param name="stream"></param>
272+
[JSInvokable]
273+
public async Task ImageUpload(string name, string contentType, long size, IJSStreamReference stream)
274+
{
275+
var data = await stream.OpenReadStreamAsync(size);
276+
var file = new EditorUploadFile(name, contentType, size, data);
277+
if (OnFileUpload != null)
278+
{
279+
await OnFileUpload(file);
280+
}
281+
}
282+
259283
/// <summary>
260284
/// 执行 editor 的方法
261285
/// </summary>

src/components/BootstrapBlazor.SummerNote/Components/Editor/Editor.razor.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ if (window.BootstrapBlazor === void 0) {
77
window.BootstrapBlazor = {};
88
}
99

10-
export async function init(id, invoker, methodGetPluginAttrs, methodClickPluginItem, height, value, lang, langUrl) {
10+
export async function init(id, invoker, methodGetPluginAttrs, methodClickPluginItem, height, value, lang, langUrl, hasUpload) {
1111
const el = document.getElementById(id)
1212
if (el === null) {
1313
return
@@ -46,6 +46,26 @@ export async function init(id, invoker, methodGetPluginAttrs, methodClickPluginI
4646
const showSubmit = el.getAttribute("data-bb-submit") === "true"
4747
option.toolbar = toolbar;
4848
reloadCallbacks(id, option);
49+
if (hasUpload) {
50+
option.callbacks.onImageUpload = async files => {
51+
editor.files = files
52+
for (let i = 0; i < files.length; i++) {
53+
const file = files[i];
54+
const image = createImage(file);
55+
editor.$editor.summernote('insertNode', image);
56+
57+
const buffer = await file.arrayBuffer();
58+
const stream = DotNet.createJSStreamReference(buffer);
59+
await editor.invoker.invokeMethodAsync('ImageUpload',
60+
file.name,
61+
file.type || 'application/octet-stream',
62+
file.size,
63+
stream
64+
)
65+
}
66+
}
67+
}
68+
4969
if (!showSubmit) {
5070
const externalOnChange = option.callbacks.onChange;
5171
option.callbacks.onChange = function (contents) {
@@ -125,7 +145,7 @@ export async function init(id, invoker, methodGetPluginAttrs, methodClickPluginI
125145
}
126146

127147
const reloadCallbacks = (id, option) => {
128-
const events = ['Blur', 'BlurCodeview', 'Change', 'ChangeCodeview', 'DialogShown', 'Enter', 'Focus', 'ImageLinkInsert', 'ImageUploadError', 'Init', 'Keydown', 'Keyup', 'Mousedown', 'Mouseup', 'Paste', 'Scroll'];
148+
const events = ['Blur', 'BlurCodeview', 'Change', 'ChangeCodeview', 'DialogShown', 'Enter', 'Focus', 'ImageUpload', 'ImageLinkInsert', 'ImageUploadError', 'Init', 'Keydown', 'Keyup', 'Mousedown', 'Mouseup', 'Paste', 'Scroll'];
129149

130150
events.forEach(event => {
131151
option.callbacks[`on${event}`] = function () {
@@ -200,6 +220,12 @@ export function dispose(id) {
200220
}
201221
}
202222

223+
const createImage = file => {
224+
const element = document.createElement('img');
225+
element.src = URL.createObjectURL(file);
226+
return element;
227+
}
228+
203229
const offEvent = eventEl => {
204230
if (eventEl) {
205231
EventHandler.off(eventEl, 'click')
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
// Website: https://www.blazor.zone or https://argozhang.github.io/
4+
5+
namespace BootstrapBlazor.Components;
6+
7+
/// <summary>
8+
/// Editor 上传文件信息类
9+
/// </summary>
10+
public class EditorUploadFile(string fileName, string contentType, long fileSize, Stream stream)
11+
{
12+
/// <summary>
13+
/// 获得/设置 文件名
14+
/// </summary>
15+
public string FileName => fileName;
16+
17+
/// <summary>
18+
/// 获得/设置 文件大小
19+
/// </summary>
20+
public long FileSize => fileSize;
21+
22+
/// <summary>
23+
/// 获得/设置 文件类型
24+
/// </summary>
25+
public string ContentType => contentType;
26+
27+
/// <summary>
28+
/// 获得/设置 上传的文件流
29+
/// </summary>
30+
public Stream Stream => stream;
31+
32+
/// <summary>
33+
/// 获得/设置 错误信息
34+
/// </summary>
35+
public Exception? Error { get; set; }
36+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
// Website: https://www.blazor.zone or https://argozhang.github.io/
4+
5+
namespace BootstrapBlazor.Components;
6+
7+
/// <summary>
8+
/// EditorUploadFile 扩展方法类
9+
/// </summary>
10+
public static class EditorUploadFileExtensions
11+
{
12+
/// <summary>
13+
/// 保存到文件方法
14+
/// </summary>
15+
/// <param name="upload"></param>
16+
/// <param name="fileName"></param>
17+
/// <param name="bufferSize"></param>
18+
/// <param name="token"></param>
19+
/// <returns></returns>
20+
public static async Task<bool> SaveToFileAsync(this EditorUploadFile upload, string fileName, int bufferSize = 64 * 1024, CancellationToken token = default)
21+
{
22+
var ret = false;
23+
24+
// 文件保护,如果文件存在则先删除
25+
if (File.Exists(fileName))
26+
{
27+
try
28+
{
29+
File.Delete(fileName);
30+
}
31+
catch (Exception ex)
32+
{
33+
upload.Error = ex;
34+
return ret;
35+
}
36+
}
37+
38+
var folder = Path.GetDirectoryName(fileName);
39+
if (!string.IsNullOrEmpty(folder) && !Directory.Exists(folder))
40+
{
41+
Directory.CreateDirectory(folder);
42+
}
43+
44+
using var uploadFile = File.OpenWrite(fileName);
45+
try
46+
{
47+
// 打开文件流
48+
var buffer = new byte[bufferSize];
49+
int bytesRead = 0;
50+
51+
// 开始读取文件
52+
while ((bytesRead = await upload.Stream.ReadAsync(buffer, token)) > 0)
53+
{
54+
await uploadFile.WriteAsync(buffer.AsMemory(0, bytesRead), token);
55+
}
56+
ret = true;
57+
}
58+
catch (Exception ex)
59+
{
60+
upload.Error = ex;
61+
}
62+
63+
return ret;
64+
}
65+
}

0 commit comments

Comments
 (0)