diff --git a/packages/secretnote-lite/package.json b/packages/secretnote-lite/package.json
index 9161a1e0..f9ff756a 100644
--- a/packages/secretnote-lite/package.json
+++ b/packages/secretnote-lite/package.json
@@ -1,6 +1,6 @@
{
"name": "@alipay/secretnote-lite",
- "version": "0.0.44",
+ "version": "0.0.45",
"license": "Apache-2.0",
"author": "vectorse@126.com",
"repository": "https://github.com/secretflow/secretnote/tree/main/packages/secretnote",
diff --git a/packages/secretnote-lite/src/modules/file/service.ts b/packages/secretnote-lite/src/modules/file/service.ts
index 0258a3b2..1e735926 100644
--- a/packages/secretnote-lite/src/modules/file/service.ts
+++ b/packages/secretnote-lite/src/modules/file/service.ts
@@ -93,7 +93,15 @@ export class FileService {
return list.content.some((file: any) => file.name === name);
}
- async uploadFile(nodeData: DataNode, name: string, content: string) {
+ /**
+ * Upload file via Jupyter Server. For binary files, use `format='base64'` and base64-encoded `content`.
+ */
+ async uploadFile(
+ nodeData: DataNode,
+ name: string,
+ content: string,
+ format: 'text' | 'base64' = 'text',
+ ) {
const serverId = nodeData.key as string;
const server = await this.serverManager.getServerDetail(serverId);
if (server) {
@@ -105,7 +113,8 @@ export class FileService {
name,
path,
type: 'file',
- format: 'text',
+ format,
+ ...(format === 'base64' ? { mimetype: 'application/octet-stream' } : {}),
});
}
}
diff --git a/packages/secretnote-lite/src/modules/file/view.tsx b/packages/secretnote-lite/src/modules/file/view.tsx
index 3d53e72e..3ce1ffc3 100644
--- a/packages/secretnote-lite/src/modules/file/view.tsx
+++ b/packages/secretnote-lite/src/modules/file/view.tsx
@@ -81,11 +81,18 @@ export const FileComponent = () => {
}
};
- const uploadFile = async (nodeData: DataNode, file: File) => {
+ /**
+ * Handle the upload file action.
+ */
+ const uploadFile = async (
+ nodeData: DataNode,
+ file: File,
+ format: 'text' | 'base64' = 'text',
+ ) => {
setIsUploading(true);
- const content = await readFile(file);
+ const content = await readFile(file, format);
try {
- await fileService.uploadFile(nodeData, file.name, content);
+ await fileService.uploadFile(nodeData, file.name, content, format);
await fileService.getFileTree();
message.success(l10n.t('文件上传成功'));
} catch (e) {
@@ -95,7 +102,7 @@ export const FileComponent = () => {
}
};
- const uploadRender = (nodeData: DataNode) => {
+ const uploadRender = (nodeData: DataNode, format: 'text' | 'base64' = 'text') => {
const props: UploadProps = {
beforeUpload: async (file) => {
const isExisted = await fileService.isFileExist(nodeData, file.name);
@@ -110,19 +117,25 @@ export const FileComponent = () => {
cancelText: l10n.t('取消'),
okType: 'danger',
async onOk(close) {
- await uploadFile(nodeData, file);
+ await uploadFile(nodeData, file, format);
return close(Promise.resolve);
},
});
} else {
- uploadFile(nodeData, file);
+ uploadFile(nodeData, file, format);
}
return false;
},
fileList: [],
};
- return {l10n.t('上传到文件夹')};
+ return (
+
+ {l10n.t(
+ format === 'text' ? '上传到文件夹 (文本文件)' : '上传到文件夹 (二进制文件)',
+ )}
+
+ );
};
const getFileIcon = (nodeData: DataNode) => {
@@ -142,8 +155,13 @@ export const FileComponent = () => {
const folderMenuItems: Menu[] = [
{
- key: 'upload',
- label: uploadRender(nodeData),
+ key: 'uploadText',
+ label: uploadRender(nodeData, 'text'),
+ icon: ,
+ },
+ {
+ key: 'uploadBinary',
+ label: uploadRender(nodeData, 'base64'),
icon: ,
},
];
diff --git a/packages/secretnote-lite/src/utils/file.ts b/packages/secretnote-lite/src/utils/file.ts
index 5e46ddaa..7e12de79 100644
--- a/packages/secretnote-lite/src/utils/file.ts
+++ b/packages/secretnote-lite/src/utils/file.ts
@@ -39,14 +39,29 @@ const saveAs = (blob: Blob, filename: string) => {
}
};
-export async function readFile(file: File): Promise {
+/**
+ * Read file as text or base64 string.
+ */
+export async function readFile(
+ file: File,
+ format: 'text' | 'base64' = 'text',
+): Promise {
return await new Promise((resolve, reject) => {
try {
const reader = new FileReader();
- reader.addEventListener('loadend', () => {
- resolve(reader.result?.toString() || '');
- });
- reader.readAsText(file);
+ if (format === 'text') {
+ reader.addEventListener('loadend', () => {
+ resolve(reader.result?.toString() || '');
+ });
+ reader.readAsText(file);
+ } else if (format === 'base64') {
+ reader.addEventListener('loadend', () => {
+ // remove base64 prelude added by the browser
+ const base64 = (reader.result as string).replace(/data:.*base64,/, '');
+ resolve(base64 || '');
+ });
+ reader.readAsDataURL(file);
+ }
} catch (e) {
reject(e);
}