github-actions[bot] commited on
Commit
01ab30b
·
1 Parent(s): 1e6d48a

Update from GitHub Actions

Browse files
functions/api/hf/[[path]].ts CHANGED
@@ -1,13 +1,12 @@
 
 
 
 
 
 
 
 
1
 
2
- //没有固定的文档
3
- //接口是通过查看huggingface.co的请求来实现的
4
- //有个huggingface hub api
5
- //https://huggingface.co/docs/huggingface.js/hub/README
6
- //https://github.com/huggingface/huggingface.js/tree/main/packages/hub
7
- //https://github.com/huggingface/huggingface.js/blob/main/packages/hub/src/lib/list-files.ts
8
- //https://github.com/huggingface/huggingface.js/blob/main/packages/hub/src/lib/download-file.ts
9
- //https://github.com/huggingface/huggingface.js/blob/main/packages/hub/src/lib/commit.ts
10
- //https://github.com/huggingface/huggingface.js/blob/main/packages/hub/src/lib/delete-files.ts
11
  export const onRequest = async (context: RouteContext): Promise<Response> => {
12
  const request = context.request;
13
  const env = context.env as Env;
@@ -39,71 +38,118 @@ export const onRequest = async (context: RouteContext): Promise<Response> => {
39
  headers: { 'Content-Type': 'application/json' }
40
  });
41
  }
42
- // Hugging Face API 基础 URL
43
- const hfApiBaseUrl = 'https://huggingface.co/api/datasets';
44
-
45
- // 处理 GET 请求 - 获取文件内容或列出文件
46
 
 
 
 
 
 
 
 
 
47
 
 
48
  if (operation === 'raw' && request.method === 'GET') {
49
  const path = pathParts.length > 6 ? pathParts.slice(6).join('/') : '';
50
- //这里没有错..getraw就是没有api
51
- const hfUrl = `https://huggingface.co/datasets/${owner}/${repo}/raw/${ref}/${path}`;
52
-
53
- const response = await fetch(hfUrl, {
54
- headers: {
55
- 'Authorization': `Bearer ${hfToken}`,
56
- 'User-Agent': 'Cloudflare-Worker'
 
 
 
 
 
57
  }
58
- });
59
-
60
- return new Response(await response.text(), {
61
- status: response.status,
62
- headers: response.headers
63
- });
 
64
  }
65
 
66
  if (operation === 'tree' && request.method === 'GET') {
67
  const path = pathParts.length > 6 ? pathParts.slice(6).join('/') : '';
68
- // 4. 列出文件
69
- const hfUrl = `${hfApiBaseUrl}/${owner}/${repo}/tree/${ref}/${path}`;
70
-
71
- const response = await fetch(hfUrl, {
72
- headers: {
73
- 'Authorization': `Bearer ${hfToken}`,
74
- 'User-Agent': 'Cloudflare-Worker'
 
 
75
  }
76
- });
77
 
78
- const data = await response.json();
79
- return new Response(JSON.stringify(data), {
80
- status: response.status,
81
- headers: { 'Content-Type': 'application/json' }
82
- });
 
 
 
 
83
  }
84
 
85
  // 处理 POST 请求 - 上传文件
86
  if (operation === 'commit' && (request.method === 'POST' || request.method === 'PUT' || request.method === 'DELETE')) {
87
- const hfUrl = `${hfApiBaseUrl}/${owner}/${repo}/commit/${ref}`;
88
- const body = await request.json();
89
- const response = await fetch(hfUrl, {
90
- method: 'POST',
91
- headers: {
92
- 'Authorization': `Bearer ${hfToken}`,
93
- 'Content-Type': 'application/json',
94
- 'User-Agent': 'Cloudflare-Worker'
95
- },
96
- body: JSON.stringify(body)
97
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
- const data = await response.json();
100
- return new Response(JSON.stringify(data), {
101
- status: response.status,
102
- headers: { 'Content-Type': 'application/json' }
103
- });
 
 
 
 
 
 
 
 
 
 
 
 
104
  }
105
 
106
-
107
  // 不支持的请求方法或路径
108
  return new Response(JSON.stringify({ error: '不支持的请求方法或路径' }), {
109
  status: 400,
 
1
+ import { downloadFile, commit, listFiles, RepoType, CommitOperation } from "@huggingface/hub";
2
+
3
+ interface CommitBody {
4
+ path: string;
5
+ content?: string;
6
+ message?: string;
7
+ lfs?: boolean;
8
+ }
9
 
 
 
 
 
 
 
 
 
 
10
  export const onRequest = async (context: RouteContext): Promise<Response> => {
11
  const request = context.request;
12
  const env = context.env as Env;
 
38
  headers: { 'Content-Type': 'application/json' }
39
  });
40
  }
 
 
 
 
41
 
42
+ const apiParams = {
43
+ repo: {
44
+ name: `${owner}/${repo}`,
45
+ type: 'dataset' as RepoType
46
+ },
47
+ accessToken: hfToken,
48
+ revision: ref
49
+ };
50
 
51
+ // 处理 GET 请求 - 获取文件内容或列出文件
52
  if (operation === 'raw' && request.method === 'GET') {
53
  const path = pathParts.length > 6 ? pathParts.slice(6).join('/') : '';
54
+
55
+ try {
56
+ const response = await downloadFile({
57
+ ...apiParams,
58
+ path: path
59
+ });
60
+
61
+ if (!response) {
62
+ return new Response(JSON.stringify({ error: '文件不存在' }), {
63
+ status: 404,
64
+ headers: { 'Content-Type': 'application/json' }
65
+ });
66
  }
67
+ return response;
68
+ } catch (error: any) {
69
+ return new Response(JSON.stringify({ error: '获取文件失败', details: error.message }), {
70
+ status: 500,
71
+ headers: { 'Content-Type': 'application/json' }
72
+ });
73
+ }
74
  }
75
 
76
  if (operation === 'tree' && request.method === 'GET') {
77
  const path = pathParts.length > 6 ? pathParts.slice(6).join('/') : '';
78
+ try {
79
+ const cursor = listFiles({
80
+ ...apiParams,
81
+ path: path
82
+ });
83
+
84
+ const files = [];
85
+ for await (const entry of cursor) {
86
+ files.push(entry);
87
  }
 
88
 
89
+ return new Response(JSON.stringify(files), {
90
+ headers: { 'Content-Type': 'application/json' }
91
+ });
92
+ } catch (error: any) {
93
+ return new Response(JSON.stringify({ error: '列出文件失败', details: error.message }), {
94
+ status: 500,
95
+ headers: { 'Content-Type': 'application/json' }
96
+ });
97
+ }
98
  }
99
 
100
  // 处理 POST 请求 - 上传文件
101
  if (operation === 'commit' && (request.method === 'POST' || request.method === 'PUT' || request.method === 'DELETE')) {
102
+ const body = await request.json() as CommitBody;
103
+
104
+ try {
105
+ let operation: CommitOperation;
106
+
107
+ if (request.method === 'DELETE') {
108
+ operation = {
109
+ operation: 'delete',
110
+ path: body.path
111
+ };
112
+ } else {
113
+ const content = body.content || '';
114
+ let blobContent: Blob;
115
+
116
+ if (body.lfs === true && body.content) {
117
+ const binary = atob(body.content);
118
+ const bytes = new Uint8Array(binary.length);
119
+ for (let i = 0; i < binary.length; i++) {
120
+ bytes[i] = binary.charCodeAt(i);
121
+ }
122
+ blobContent = new Blob([bytes]);
123
+ } else {
124
+ blobContent = new Blob([content]);
125
+ }
126
+
127
+ operation = {
128
+ operation: 'addOrUpdate',
129
+ path: body.path,
130
+ content: blobContent
131
+ };
132
+ }
133
 
134
+ const response = await commit({
135
+ ...apiParams,
136
+ operations: [operation],
137
+ title: `${request.method === 'DELETE' ? 'Delete' : 'Update'} ${body.path}`,
138
+ description: body.message || `Changed via API`
139
+ });
140
+
141
+ return new Response(JSON.stringify(response), {
142
+ status: 200,
143
+ headers: { 'Content-Type': 'application/json' }
144
+ });
145
+ } catch (error: any) {
146
+ return new Response(JSON.stringify({ error: '提交更改失败', details: error.message }), {
147
+ status: 500,
148
+ headers: { 'Content-Type': 'application/json' }
149
+ });
150
+ }
151
  }
152
 
 
153
  // 不支持的请求方法或路径
154
  return new Response(JSON.stringify({ error: '不支持的请求方法或路径' }), {
155
  status: 400,
package-lock.json CHANGED
@@ -9,6 +9,7 @@
9
  "version": "0.0.0",
10
  "dependencies": {
11
  "@hono/node-server": "^1.13.8",
 
12
  "@monaco-editor/loader": "^1.5.0",
13
  "@tailwindcss/vite": "^4.0.14",
14
  "dotenv": "^16.4.7",
@@ -796,6 +797,24 @@
796
  "hono": "^4"
797
  }
798
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
799
  "node_modules/@img/sharp-darwin-arm64": {
800
  "version": "0.33.5",
801
  "resolved": "https://registry.npmmirror.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
 
9
  "version": "0.0.0",
10
  "dependencies": {
11
  "@hono/node-server": "^1.13.8",
12
+ "@huggingface/hub": "^1.1.2",
13
  "@monaco-editor/loader": "^1.5.0",
14
  "@tailwindcss/vite": "^4.0.14",
15
  "dotenv": "^16.4.7",
 
797
  "hono": "^4"
798
  }
799
  },
800
+ "node_modules/@huggingface/hub": {
801
+ "version": "1.1.2",
802
+ "resolved": "https://registry.npmmirror.com/@huggingface/hub/-/hub-1.1.2.tgz",
803
+ "integrity": "sha512-Jf4GhvVj9ABDw4Itb3BV1T7f22iewuZva476qTicQ4kOTbosuUuFDhsVH7ZH6rVNgg20Ll9kaNBF5CXjySIT+w==",
804
+ "license": "MIT",
805
+ "dependencies": {
806
+ "@huggingface/tasks": "^0.18.2"
807
+ },
808
+ "engines": {
809
+ "node": ">=18"
810
+ }
811
+ },
812
+ "node_modules/@huggingface/tasks": {
813
+ "version": "0.18.3",
814
+ "resolved": "https://registry.npmmirror.com/@huggingface/tasks/-/tasks-0.18.3.tgz",
815
+ "integrity": "sha512-WCIg6tOSftCkE2WxSaDIZRsrs6bkHiH2qc4t6r8L/xoebAhQmIPEDU700zXI7ac1+I6UcCponYxn/oqu3TGNxQ==",
816
+ "license": "MIT"
817
+ },
818
  "node_modules/@img/sharp-darwin-arm64": {
819
  "version": "0.33.5",
820
  "resolved": "https://registry.npmmirror.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
package.json CHANGED
@@ -16,6 +16,7 @@
16
  },
17
  "dependencies": {
18
  "@hono/node-server": "^1.13.8",
 
19
  "@monaco-editor/loader": "^1.5.0",
20
  "@tailwindcss/vite": "^4.0.14",
21
  "dotenv": "^16.4.7",
 
16
  },
17
  "dependencies": {
18
  "@hono/node-server": "^1.13.8",
19
+ "@huggingface/hub": "^1.1.2",
20
  "@monaco-editor/loader": "^1.5.0",
21
  "@tailwindcss/vite": "^4.0.14",
22
  "dotenv": "^16.4.7",
src/services/repoApi.ts CHANGED
@@ -30,6 +30,25 @@ interface IRepoApi {
30
  updateFile(account: Account, path: string, content: string, sha: string, message?: string): Promise<any>;
31
  deleteFile(account: Account, path: string, sha: string, message?: string): Promise<any>;
32
  createFile(account: Account, path: string, content: string, message?: string): Promise<any>;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
 
35
  // GitHub implementation
@@ -60,7 +79,6 @@ class GitHubRepoApi implements IRepoApi {
60
  }
61
 
62
  async updateFile(account: Account, path: string, content: string, sha: string, message?: string) {
63
-
64
  const encoder = new TextEncoder();
65
  const bytes = encoder.encode(content);
66
  const base64Content = btoa(String.fromCharCode.apply(null, [...new Uint8Array(bytes)]));
@@ -95,19 +113,29 @@ class GitHubRepoApi implements IRepoApi {
95
  }
96
 
97
  async createFile(account: Account, path: string, content: string, message?: string) {
 
 
 
98
 
99
- let encodedContent;
100
- try {
101
- // 检查内容是否已经是 Base64 编码
102
- atob(content);
103
- encodedContent = content;
104
- } catch (e) {
105
- // 如果不是,则编码它
106
- const encoder = new TextEncoder();
107
- const bytes = encoder.encode(content);
108
- encodedContent = btoa(String.fromCharCode.apply(null, [...new Uint8Array(bytes)]));
109
- }
110
-
 
 
 
 
 
 
 
111
  const response = await fetch(
112
  `${API_BASE_URL}/api/github/${account.owner}/${account.repo}/${path}`,
113
  {
@@ -125,7 +153,6 @@ class GitHubRepoApi implements IRepoApi {
125
 
126
  // HuggingFace implementation
127
  class HuggingFaceRepoApi implements IRepoApi {
128
-
129
  async getContents(account: Account, path: string = ''): Promise<RepoContent[]> {
130
  const response = await fetch(
131
  `${API_BASE_URL}/api/hf/${account.owner}/${account.repo}/tree/${account.ref}/${path}`,
@@ -141,13 +168,14 @@ class HuggingFaceRepoApi implements IRepoApi {
141
  return result.map((item: any) => {
142
  item.sha = item.oid;
143
  item.name = item.path.split('/').pop() || '';
 
144
  return item;
145
  });
146
  }
147
 
148
  async getFileContent(account: Account, path: string): Promise<RepoContent> {
149
- const response = await fetch(
150
- `${API_BASE_URL}/api/hf/${account.owner}/${account.repo}/raw/${account.ref}/${path}`,
151
  {
152
  method: 'GET',
153
  headers: {
@@ -163,6 +191,24 @@ class HuggingFaceRepoApi implements IRepoApi {
163
  throw new Error('认证失败,请重新登录');
164
  }
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  return {
167
  content: await response.text(),
168
  encoding: 'utf-8',
@@ -188,15 +234,10 @@ class HuggingFaceRepoApi implements IRepoApi {
188
  Authorization: `Bearer ${account.token}`
189
  },
190
  body: JSON.stringify({
191
- "description": "",
192
- "summary": message,
193
- "files": [
194
- {
195
- "content": content,
196
- "encoding": "utf-8",
197
- "path": path
198
- }
199
- ]
200
  })
201
  }
202
  );
@@ -218,13 +259,9 @@ class HuggingFaceRepoApi implements IRepoApi {
218
  Authorization: `Bearer ${account.token}`
219
  },
220
  body: JSON.stringify({
221
- "description": "",
222
- "summary": message,
223
- "deletedFiles": [
224
- {
225
- "path": path
226
- }
227
- ]
228
  })
229
  }
230
  );
@@ -241,15 +278,36 @@ class HuggingFaceRepoApi implements IRepoApi {
241
  Authorization: `Bearer ${account.token}`
242
  },
243
  body: JSON.stringify({
244
- "description": "",
245
- "summary": message,
246
- "files": [
247
- {
248
- "content": content,
249
- "encoding": "utf-8",
250
- "path": path
251
- }
252
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  })
254
  }
255
  );
@@ -310,5 +368,10 @@ export const repoApi = {
310
  async createFile(account: Account, path: string, content: string, message?: string) {
311
  const api = RepoApiFactory.getRepoApi(account.type);
312
  return api.createFile(account, path, content, message);
 
 
 
 
 
313
  }
314
  };
 
30
  updateFile(account: Account, path: string, content: string, sha: string, message?: string): Promise<any>;
31
  deleteFile(account: Account, path: string, sha: string, message?: string): Promise<any>;
32
  createFile(account: Account, path: string, content: string, message?: string): Promise<any>;
33
+ createAsset(account: Account, path: string, content: Uint8Array, message?: string): Promise<any>;
34
+ }
35
+
36
+ // 浏览器环境中将 ArrayBuffer/Uint8Array 转换为 Base64 的辅助方法
37
+ function arrayBufferToBase64(buffer: Uint8Array): Promise<string> {
38
+ return new Promise((resolve, reject) => {
39
+ const blob = new Blob([buffer]);
40
+ const reader = new FileReader();
41
+
42
+ reader.onload = () => {
43
+ const dataUrl = reader.result as string;
44
+ // 移除 data URL 前缀 (例如 "data:image/png;base64,")
45
+ const base64 = dataUrl.split(',')[1];
46
+ resolve(base64);
47
+ };
48
+
49
+ reader.onerror = reject;
50
+ reader.readAsDataURL(blob);
51
+ });
52
  }
53
 
54
  // GitHub implementation
 
79
  }
80
 
81
  async updateFile(account: Account, path: string, content: string, sha: string, message?: string) {
 
82
  const encoder = new TextEncoder();
83
  const bytes = encoder.encode(content);
84
  const base64Content = btoa(String.fromCharCode.apply(null, [...new Uint8Array(bytes)]));
 
113
  }
114
 
115
  async createFile(account: Account, path: string, content: string, message?: string) {
116
+ const encoder = new TextEncoder();
117
+ const bytes = encoder.encode(content);
118
+ const encodedContent = btoa(String.fromCharCode.apply(null, [...new Uint8Array(bytes)]));
119
 
120
+ const response = await fetch(
121
+ `${API_BASE_URL}/api/github/${account.owner}/${account.repo}/${path}`,
122
+ {
123
+ method: 'POST',
124
+ headers: {
125
+ 'Content-Type': 'application/json',
126
+ Authorization: `Bearer ${account.token}`
127
+ },
128
+ body: JSON.stringify({ branch: account.ref, content: encodedContent, message })
129
+ }
130
+ );
131
+ return handleResponse(response);
132
+ }
133
+
134
+
135
+
136
+ async createAsset(account: Account, path: string, content: Uint8Array, message?: string) {
137
+
138
+ const encodedContent = await arrayBufferToBase64(content);
139
  const response = await fetch(
140
  `${API_BASE_URL}/api/github/${account.owner}/${account.repo}/${path}`,
141
  {
 
153
 
154
  // HuggingFace implementation
155
  class HuggingFaceRepoApi implements IRepoApi {
 
156
  async getContents(account: Account, path: string = ''): Promise<RepoContent[]> {
157
  const response = await fetch(
158
  `${API_BASE_URL}/api/hf/${account.owner}/${account.repo}/tree/${account.ref}/${path}`,
 
168
  return result.map((item: any) => {
169
  item.sha = item.oid;
170
  item.name = item.path.split('/').pop() || '';
171
+ item.type = item.type === 'directory' ? 'dir' : 'file';
172
  return item;
173
  });
174
  }
175
 
176
  async getFileContent(account: Account, path: string): Promise<RepoContent> {
177
+ const url = `${API_BASE_URL}/api/hf/${account.owner}/${account.repo}/raw/${account.ref}/${path}`;
178
+ const response = await fetch(url,
179
  {
180
  method: 'GET',
181
  headers: {
 
191
  throw new Error('认证失败,请重新登录');
192
  }
193
 
194
+
195
+ //根据响应的内容类型设置encoding
196
+ const contentType = response.headers.get('content-type');
197
+ if (contentType && contentType.startsWith('image/')) {
198
+ return {
199
+ content: await arrayBufferToBase64(await response.bytes()),
200
+ download_url: null,
201
+ encoding: 'base64',
202
+ name: '',
203
+ path: path,
204
+ sha: '',
205
+ size: 0,
206
+ url: '',
207
+ html_url: '',
208
+ git_url: '',
209
+ type: 'file'
210
+ };
211
+ }
212
  return {
213
  content: await response.text(),
214
  encoding: 'utf-8',
 
234
  Authorization: `Bearer ${account.token}`
235
  },
236
  body: JSON.stringify({
237
+ "lfs": false,
238
+ "path": path,
239
+ "content": content,
240
+ "message": message,
 
 
 
 
 
241
  })
242
  }
243
  );
 
259
  Authorization: `Bearer ${account.token}`
260
  },
261
  body: JSON.stringify({
262
+ "lfs": false,
263
+ "path": path,
264
+ "message": message,
 
 
 
 
265
  })
266
  }
267
  );
 
278
  Authorization: `Bearer ${account.token}`
279
  },
280
  body: JSON.stringify({
281
+ "lfs": false,
282
+ "path": path,
283
+ "content": content,
284
+ "message": message,
285
+ })
286
+ }
287
+ );
288
+ const result = await handleResponse(response);
289
+ return {
290
+ content: {
291
+ sha: result.commitOid
292
+ }
293
+ };
294
+ }
295
+
296
+ async createAsset(account: Account, path: string, content: Uint8Array, message?: string) {
297
+ const encodedContent = await arrayBufferToBase64(content);
298
+ const response = await fetch(
299
+ `${API_BASE_URL}/api/hf/${account.owner}/${account.repo}/commit/${account.ref}/${path}`,
300
+ {
301
+ method: 'POST',
302
+ headers: {
303
+ 'Content-Type': 'application/json',
304
+ Authorization: `Bearer ${account.token}`
305
+ },
306
+ body: JSON.stringify({
307
+ "lfs": true,
308
+ "path": path,
309
+ "content": encodedContent,
310
+ "message": message,
311
  })
312
  }
313
  );
 
368
  async createFile(account: Account, path: string, content: string, message?: string) {
369
  const api = RepoApiFactory.getRepoApi(account.type);
370
  return api.createFile(account, path, content, message);
371
+ },
372
+
373
+ async createAsset(account: Account, path: string, content: Uint8Array, message?: string) {
374
+ const api = RepoApiFactory.getRepoApi(account.type);
375
+ return api.createAsset(account, path, content, message);
376
  }
377
  };
src/views/ContentView.vue CHANGED
@@ -77,10 +77,7 @@ const fetchContent = async () => {
77
  if (isImageFile.value) {
78
  if (result.content) {
79
  // 如果有 content,将 base64 转换为 Blob URL
80
- const base64Data = result.encoding === 'base64' ?
81
- result.content :
82
- btoa(result.content);
83
- const byteCharacters = atob(base64Data);
84
  const byteNumbers = new Array(byteCharacters.length);
85
  for (let i = 0; i < byteCharacters.length; i++) {
86
  byteNumbers[i] = byteCharacters.charCodeAt(i);
@@ -353,6 +350,7 @@ const isImageFile = computed(() => {
353
  watch(() => route.query, async (query) => {
354
  // Only respond to query changes when on the repo route
355
  if (route.path !== '/content') return;
 
356
  const { id, path, newFile } = query;
357
  isNewFile.value = !!newFile;
358
  if (id) {
@@ -388,6 +386,7 @@ onUnmounted(() => {
388
  onBeforeUnmount(() => {
389
  if (imageUrl.value && imageUrl.value.startsWith('blob:')) {
390
  URL.revokeObjectURL(imageUrl.value);
 
391
  }
392
  });
393
  </script>
 
77
  if (isImageFile.value) {
78
  if (result.content) {
79
  // 如果有 content,将 base64 转换为 Blob URL
80
+ const byteCharacters = atob(result.content);
 
 
 
81
  const byteNumbers = new Array(byteCharacters.length);
82
  for (let i = 0; i < byteCharacters.length; i++) {
83
  byteNumbers[i] = byteCharacters.charCodeAt(i);
 
350
  watch(() => route.query, async (query) => {
351
  // Only respond to query changes when on the repo route
352
  if (route.path !== '/content') return;
353
+ imageUrl.value = '';
354
  const { id, path, newFile } = query;
355
  isNewFile.value = !!newFile;
356
  if (id) {
 
386
  onBeforeUnmount(() => {
387
  if (imageUrl.value && imageUrl.value.startsWith('blob:')) {
388
  URL.revokeObjectURL(imageUrl.value);
389
+ imageUrl.value = '';
390
  }
391
  });
392
  </script>
src/views/UploadView.vue CHANGED
@@ -94,23 +94,18 @@ const handleConfirmUpload = async () => {
94
  throw new Error('Invalid file object');
95
  }
96
 
97
- const content = await new Promise<string>((resolve, reject) => {
98
  const reader = new FileReader();
99
  reader.onload = (e) => {
100
  if (e.target?.result) {
101
- // 获取 base64 字符串,移除 "data:*/*;base64," 前缀
102
- const dataUrl = e.target.result.toString();
103
-
104
- const base64 = dataUrl.split(',')[1];
105
-
106
- resolve(base64);
107
  } else {
108
  reject(new Error('Failed to read file content'));
109
  }
110
  };
111
  reader.onerror = () => reject(reader.error);
112
  // 使用 readAsDataURL 来处理所有类型的文件
113
- reader.readAsDataURL(selectedFile.value as File);
114
  });
115
 
116
  const filePath = currentPath.value
@@ -122,7 +117,7 @@ const handleConfirmUpload = async () => {
122
  `上传文件: ${selectedFile.value.name}`;
123
 
124
  // GitHub API 要求 base64 编码的内容
125
- const response = await repoApi.createFile(
126
  account,
127
  filePath,
128
  content,
 
94
  throw new Error('Invalid file object');
95
  }
96
 
97
+ const content = await new Promise<Uint8Array>((resolve, reject) => {
98
  const reader = new FileReader();
99
  reader.onload = (e) => {
100
  if (e.target?.result) {
101
+ resolve(e.target.result as Uint8Array);
 
 
 
 
 
102
  } else {
103
  reject(new Error('Failed to read file content'));
104
  }
105
  };
106
  reader.onerror = () => reject(reader.error);
107
  // 使用 readAsDataURL 来处理所有类型的文件
108
+ reader.readAsArrayBuffer(selectedFile.value as File);
109
  });
110
 
111
  const filePath = currentPath.value
 
117
  `上传文件: ${selectedFile.value.name}`;
118
 
119
  // GitHub API 要求 base64 编码的内容
120
+ const response = await repoApi.createAsset(
121
  account,
122
  filePath,
123
  content,