添加filesystem

This commit is contained in:
2025-04-14 18:04:04 +08:00
parent 3b2e72127f
commit b76a685988
18 changed files with 1173 additions and 25 deletions

View File

@ -0,0 +1,168 @@
/* eslint-disable no-unused-vars */
import IoC from "@App/app/ioc";
import { SystemConfig } from "@App/pkg/config/config";
import { AuthVerify } from "../auth";
import FileSystem, { File, FileReader, FileWriter } from "../filesystem";
import { joinPath } from "../utils";
import { OneDriveFileReader, OneDriveFileWriter } from "./rw";
export default class OneDriveFileSystem implements FileSystem {
accessToken?: string;
path: string;
systemConfig: SystemConfig;
constructor(path?: string, accessToken?: string) {
this.path = path || "/";
this.accessToken = accessToken;
this.systemConfig = IoC.instance(SystemConfig) as SystemConfig;
}
async verify(): Promise<void> {
const token = await AuthVerify("onedrive");
this.accessToken = token;
return this.list().then();
}
open(file: File): Promise<FileReader> {
return Promise.resolve(new OneDriveFileReader(this, file));
}
openDir(path: string): Promise<FileSystem> {
if (path.startsWith("ScriptCat")) {
path = path.substring(9);
}
return Promise.resolve(
new OneDriveFileSystem(joinPath(this.path, path), this.accessToken)
);
}
create(path: string): Promise<FileWriter> {
return Promise.resolve(
new OneDriveFileWriter(this, joinPath(this.path, path))
);
}
createDir(dir: string): Promise<void> {
if (dir && dir.startsWith("ScriptCat")) {
dir = dir.substring(9);
if (dir.startsWith("/")) {
dir = dir.substring(1);
}
}
if (!dir) {
return Promise.resolve();
}
dir = joinPath(this.path, dir);
const dirs = dir.split("/");
let parent = "";
if (dirs.length > 2) {
parent = dirs.slice(0, dirs.length - 1).join("/");
}
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
if (parent !== "") {
parent = `:${parent}:`;
}
return this.request(
`https://graph.microsoft.com/v1.0/me/drive/special/approot${parent}/children`,
{
method: "POST",
headers: myHeaders,
body: JSON.stringify({
name: dirs[dirs.length - 1],
folder: {},
"@microsoft.graph.conflictBehavior": "replace",
}),
}
).then((data: any) => {
if (data.errno) {
throw new Error(JSON.stringify(data));
}
return Promise.resolve();
});
}
// eslint-disable-next-line no-undef
request(url: string, config?: RequestInit, nothen?: boolean) {
config = config || {};
const headers = <Headers>config.headers || new Headers();
if (url.indexOf("uploadSession") === -1) {
headers.append(`Authorization`, `Bearer ${this.accessToken}`);
}
config.headers = headers;
const ret = fetch(url, config);
if (nothen) {
return <Promise<Response>>ret;
}
return ret
.then((data) => data.json())
.then(async (data) => {
if (data.error) {
if (data.error.code === "InvalidAuthenticationToken") {
const token = await AuthVerify("onedrive", true);
this.accessToken = token;
headers.set(`Authorization`, `Bearer ${this.accessToken}`);
return fetch(url, config)
.then((retryData) => retryData.json())
.then((retryData) => {
if (retryData.error) {
throw new Error(JSON.stringify(retryData));
}
return data;
});
}
throw new Error(JSON.stringify(data));
}
return data;
});
}
delete(path: string): Promise<void> {
return this.request(
`https://graph.microsoft.com/v1.0/me/drive/special/approot:${joinPath(
this.path,
path
)}`,
{
method: "DELETE",
},
true
).then(async (resp) => {
if (resp.status !== 204) {
throw new Error(await resp.text());
}
return resp;
});
}
list(): Promise<File[]> {
let { path } = this;
if (path === "/") {
path = "";
} else {
path = `:${path}:`;
}
return this.request(
`https://graph.microsoft.com/v1.0/me/drive/special/approot${path}/children`
).then((data) => {
const list: File[] = [];
data.value.forEach((val: any) => {
list.push({
name: val.name,
path: this.path,
size: val.size,
digest: val.eTag,
createtime: new Date(val.createdDateTime).getTime(),
updatetime: new Date(val.lastModifiedDateTime).getTime(),
});
});
return list;
});
}
getDirUrl(): Promise<string> {
throw new Error("Method not implemented.");
}
}

View File

@ -0,0 +1,105 @@
/* eslint-disable max-classes-per-file */
/* eslint-disable import/prefer-default-export */
import { calculateMd5 } from "@App/pkg/utils/utils";
import { MD5 } from "crypto-js";
import { File, FileReader, FileWriter } from "../filesystem";
import { joinPath } from "../utils";
import OneDriveFileSystem from "./onedrive";
export class OneDriveFileReader implements FileReader {
file: File;
fs: OneDriveFileSystem;
constructor(fs: OneDriveFileSystem, file: File) {
this.fs = fs;
this.file = file;
}
async read(type?: "string" | "blob"): Promise<string | Blob> {
const data = await this.fs.request(
`https://graph.microsoft.com/v1.0/me/drive/special/approot:${joinPath(
this.file.path,
this.file.name
)}:/content`,
{},
true
);
if (data.status !== 200) {
return Promise.reject(await data.text());
}
switch (type) {
case "string":
return data.text();
default: {
return data.blob();
}
}
}
}
export class OneDriveFileWriter implements FileWriter {
path: string;
fs: OneDriveFileSystem;
constructor(fs: OneDriveFileSystem, path: string) {
this.fs = fs;
this.path = path;
}
size(content: string | Blob) {
if (content instanceof Blob) {
return content.size;
}
return new Blob([content]).size;
}
async md5(content: string | Blob) {
if (content instanceof Blob) {
return calculateMd5(content);
}
return MD5(content).toString();
}
async write(content: string | Blob): Promise<void> {
// 预上传获取id
const size = this.size(content).toString();
let myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
const uploadUrl = await this.fs
.request(
`https://graph.microsoft.com/v1.0/me/drive/special/approot:${this.path}:/createUploadSession`,
{
method: "POST",
headers: myHeaders,
body: JSON.stringify({
item: {
"@microsoft.graph.conflictBehavior": "replace",
// description: "description",
// fileSystemInfo: {
// "@odata.type": "microsoft.graph.fileSystemInfo",
// },
// name: this.path.substring(this.path.lastIndexOf("/") + 1),
},
}),
}
)
.then((data) => {
if (data.error) {
throw new Error(JSON.stringify(data));
}
return data.uploadUrl;
});
myHeaders = new Headers();
myHeaders.append(
"Content-Range",
`bytes 0-${parseInt(size, 10) - 1}/${size}`
);
return this.fs.request(uploadUrl, {
method: "PUT",
body: content,
headers: myHeaders,
});
}
}