Created
October 20, 2020 11:25
-
-
Save Alex4386/1cb6b0dc7bdf35f11046f6d6d8a9b3dd to your computer and use it in GitHub Desktop.
TypeScript (a.k.a. AnyScript) Implementation of Google Drive Navigator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable @typescript-eslint/camelcase */ | |
import { drive_v3 } from "googleapis"; | |
import { GaxiosResponse } from "gaxios"; | |
import mime from "mime"; | |
import fs, { ReadStream } from "fs"; | |
import File from "./file"; | |
import path from "path"; | |
import { escapeSingleQuotes } from "../util"; | |
class Directory extends File { | |
constructor(drive: drive_v3.Drive, id?: string) { | |
super(drive, id ? id : "root"); | |
} | |
/** | |
* 현재 디렉토리를 기준으로 폴더를 하나 생성합니다 | |
* @param name 파일 이름 | |
*/ | |
public async mkdir(name: string): Promise<Directory|null> { | |
const folder = await this.drive.files.create( | |
{ | |
requestBody: { | |
name, | |
mimeType: 'application/vnd.google-apps.folder', | |
parents: [this.id] | |
}, | |
fields: "id" | |
}); | |
return (typeof folder.data.id === "string") ? new Directory(this.drive, folder.data.id) : null; | |
} | |
/** | |
* 로컬의 파일을 원격으로 업로드 합니다 | |
* | |
* @param fileName 올릴 파일 로컬 경로 이름 | |
*/ | |
public async upload(fileName: string, statusReport?: (status: number) => void): Promise<File|null> { | |
const mimeType = mime.getType(fileName); | |
const parentDirectories = [this.id]; | |
const uploadName = path.parse(fileName).base; | |
const uploadSize = fs.statSync(fileName).size; | |
const file = await this.drive.files.create({ | |
requestBody: { | |
name: uploadName, | |
parents: parentDirectories, | |
}, | |
media: { | |
mimeType: mimeType ? mimeType : "application/octet-stream", | |
body: fs.createReadStream(fileName) | |
}, | |
fields: "id", | |
}, { | |
onUploadProgress: (e) => { | |
if (typeof statusReport !== "undefined") { | |
if ((e.bytesRead * 100 * 100 / uploadSize) % 1 < 0.1) { | |
statusReport((e.bytesRead / uploadSize) * 100); | |
} | |
} | |
} | |
}); | |
return (typeof file.data.id === "string") ? new File(this.drive, file.data.id) : null; | |
} | |
/** | |
* 로컬의 파일을 원격으로 업로드 합니다 | |
* | |
* @param fileName 올릴 파일 로컬 경로 이름 | |
*/ | |
public async uploadStream(fileName: string, stream: ReadStream): Promise<File|null> { | |
const mimeType = mime.getType(fileName); | |
const parentDirectories = [this.id]; | |
const uploadName = path.parse(fileName).base; | |
const file = await this.drive.files.create({ | |
requestBody: { | |
name: uploadName, | |
parents: parentDirectories, | |
}, | |
media: { | |
mimeType: mimeType ? mimeType : "application/octet-stream", | |
body: stream | |
}, | |
fields: "id", | |
}); | |
return (typeof file.data.id === "string") ? new File(this.drive, file.data.id) : null; | |
} | |
/** | |
* 현재 디렉토리에 존재하는 파일/폴더를 array로 반환합니다. | |
*/ | |
public async list(): Promise<Array<File|Directory>> { | |
let pageToken = undefined; | |
const contents: Array<File|Directory> = []; | |
do { | |
// eslint-disable-next-line no-await-in-loop | |
const files: GaxiosResponse<drive_v3.Schema$FileList> = await this.drive.files.list({ | |
q: `'${escapeSingleQuotes(this.id)}' in parents`, | |
fields: "nextPageToken, files(id, name, parents, mimeType)", | |
pageToken | |
}); | |
if (typeof files.data.files === "undefined") break; | |
files.data.files.forEach((file) => { | |
if (typeof file.id === "undefined" || file.id === null) return; | |
if (file.mimeType === "application/vnd.google-apps.folder") { | |
contents.push(new Directory(this.drive, file.id)); | |
} else { | |
contents.push(new File(this.drive, file.id)); | |
} | |
}); | |
if (typeof files.data.nextPageToken !== "undefined" && files.data.nextPageToken !== null) { | |
pageToken = files.data.nextPageToken; | |
} | |
} while(typeof pageToken !== "undefined"); | |
return contents; | |
} | |
/** | |
* 현재 폴더에 존재하는 파일을 array로 반환합니다. | |
*/ | |
public async listFiles(): Promise<Array<File>> { | |
let pageToken = undefined; | |
const contents: Array<File> = []; | |
do { | |
// eslint-disable-next-line no-await-in-loop | |
const files: GaxiosResponse<drive_v3.Schema$FileList> = await this.drive.files.list({ | |
q: `'${escapeSingleQuotes(this.id)}' in parents and mimeType != 'application/vnd.google-apps.folder' and trashed = false`, | |
fields: "nextPageToken, files(id, name, parents, mimeType)", | |
pageToken | |
}); | |
if (typeof files.data.files === "undefined") break; | |
files.data.files.forEach((file) => { | |
if (typeof file.id === "undefined" || file.id === null) return; | |
if (file.mimeType !== "application/vnd.google-apps.folder") { | |
contents.push(new File(this.drive, file.id)); | |
} | |
}); | |
if (typeof files.data.nextPageToken !== "undefined" && files.data.nextPageToken !== null) { | |
pageToken = files.data.nextPageToken; | |
} | |
} while(typeof pageToken !== "undefined"); | |
return contents; | |
} | |
/** | |
* 현재 폴더에 존재하는 폴더를 array로 반환합니다. | |
*/ | |
public async listDir(): Promise<Array<Directory>> { | |
let pageToken = undefined; | |
const contents: Array<Directory> = []; | |
do { | |
// eslint-disable-next-line no-await-in-loop | |
const files: GaxiosResponse<drive_v3.Schema$FileList> = await this.drive.files.list({ | |
q: `'${escapeSingleQuotes(this.id)}' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false`, | |
fields: "nextPageToken, files(id, name, parents, mimeType)", | |
pageToken | |
}); | |
if (typeof files.data.files === "undefined") break; | |
files.data.files.forEach((file) => { | |
if (typeof file.id === "undefined" || file.id === null) return; | |
if (file.mimeType === "application/vnd.google-apps.folder") { | |
contents.push(new Directory(this.drive, file.id)); | |
} | |
}); | |
if (typeof files.data.nextPageToken !== "undefined" && files.data.nextPageToken !== null) { | |
pageToken = files.data.nextPageToken; | |
} | |
} while(typeof pageToken !== "undefined"); | |
return contents; | |
} | |
/** | |
* 현재 폴더에 존재하는 특정한 이름의 파일/폴더를 찾아 array로 반환합니다. | |
*/ | |
public async find(fileName: string): Promise<Array<File|Directory>> { | |
let pageToken = undefined; | |
const contents: Array<File|Directory> = []; | |
do { | |
// eslint-disable-next-line no-await-in-loop | |
const files: GaxiosResponse<drive_v3.Schema$FileList> = await this.drive.files.list({ | |
q: `'${escapeSingleQuotes(this.id)}' in parents and name = '${escapeSingleQuotes(fileName)}' and trashed = false`, | |
fields: "nextPageToken, files(id, name, parents, mimeType)", | |
pageToken | |
}); | |
if (typeof files.data.files === "undefined") break; | |
files.data.files.forEach((file) => { | |
if (file.id === undefined || file.id === null) return; | |
if (file.mimeType === "application/vnd.google-apps.folder") { | |
contents.push(new Directory(this.drive, file.id)); | |
} else { | |
contents.push(new File(this.drive, file.id)); | |
} | |
}); | |
if (typeof files.data.nextPageToken !== "undefined" && files.data.nextPageToken !== null) { | |
pageToken = files.data.nextPageToken; | |
} | |
} while(typeof pageToken !== "undefined"); | |
return contents; | |
} | |
/** | |
* 현재 폴더에 존재하는 특정한 이름의 파일/폴더가 존재하는 지 boolean으로 반환합니다. | |
*/ | |
public async exists(fileName: string): Promise<boolean> { | |
const findResults = await this.find(fileName); | |
return findResults.length > 0; | |
} | |
/** | |
* 현재 폴더에 존재하는 특정한 이름의 파일/폴더를 반환합니다. | |
*/ | |
public async get(fileName: string): Promise<File|Directory> { | |
const file = (await this.find(fileName))[0]; | |
return file; | |
} | |
/** | |
* 현재 폴더에 존재하는 특정한 이름의 파일을 반환합니다. | |
*/ | |
public async getFile(fileName: string): Promise<File> { | |
const file = (await this.findFile(fileName))[0]; | |
return file; | |
} | |
/** | |
* 현재 폴더에 존재하는 특정한 이름의 폴더를 반환합니다. | |
*/ | |
public async getDir(fileName: string): Promise<Directory> { | |
const file = (await this.findDir(fileName))[0]; | |
return file; | |
} | |
/** | |
* 현재 폴더에 존재하는 특정한 파일이름을 가진 파일들을 반환합니다. | |
* @param fileName 파일이름 | |
*/ | |
public async findFile(fileName: string): Promise<Array<File>> { | |
let pageToken = undefined; | |
const contents: Array<File> = []; | |
do { | |
// eslint-disable-next-line no-await-in-loop | |
const files: GaxiosResponse<drive_v3.Schema$FileList> = await this.drive.files.list({ | |
q: `'${escapeSingleQuotes(this.id)}' in parents and name = '${escapeSingleQuotes(fileName)}' and mimeType != 'application/vnd.google-apps.folder' and trashed = false`, | |
fields: "nextPageToken, files(id, name, parents, mimeType)", | |
pageToken | |
}); | |
if (typeof files.data.files === "undefined") break; | |
files.data.files.forEach((file) => { | |
if (typeof file.id === "undefined" || file.id === null) return; | |
if (file.mimeType !== "application/vnd.google-apps.folder") { | |
contents.push(new File(this.drive, file.id)); | |
} | |
}); | |
if (typeof files.data.nextPageToken !== "undefined" && files.data.nextPageToken !== null) { | |
pageToken = files.data.nextPageToken; | |
} | |
} while(typeof pageToken !== "undefined"); | |
return contents; | |
} | |
/** | |
* 현재 폴더에 존재하는 특정한 폴더이름을 가진 폴더들을 반환합니다. | |
* @param fileName 파일이름 | |
*/ | |
public async findDir(fileName: string): Promise<Array<Directory>> { | |
let pageToken = undefined; | |
const contents: Array<Directory> = []; | |
do { | |
// eslint-disable-next-line no-await-in-loop | |
const files: GaxiosResponse<drive_v3.Schema$FileList> = await this.drive.files.list({ | |
q: `'${escapeSingleQuotes(this.id)}' in parents and name = '${escapeSingleQuotes(fileName)}' and mimeType = 'application/vnd.google-apps.folder' and trashed = false`, | |
fields: "nextPageToken, files(id, name, parents, mimeType)", | |
pageToken | |
}); | |
if (typeof files.data.files === "undefined") break; | |
files.data.files.forEach((file) => { | |
if (typeof file.id === "undefined" || file.id === null) return; | |
if (file.mimeType === "application/vnd.google-apps.folder") { | |
contents.push(new Directory(this.drive, file.id)); | |
} | |
}); | |
if (typeof files.data.nextPageToken !== "undefined" && files.data.nextPageToken !== null) { | |
pageToken = files.data.nextPageToken; | |
} | |
} while(typeof pageToken !== "undefined"); | |
return contents; | |
} | |
} | |
export default Directory; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable @typescript-eslint/camelcase */ // Due to | |
import { drive_v3 } from "googleapis"; | |
import fs from "fs"; | |
import Directory from "./directory"; | |
class File { | |
protected drive: drive_v3.Drive; | |
public id: string; | |
constructor(drive: drive_v3.Drive, id: string) { | |
this.drive = drive; | |
this.id = id; | |
} | |
/** | |
* 현재 파일/폴더의 상위 디렉토리를 표시합니다. | |
* 최상단의 있을 경우, 루트 디렉토리가 반환됩니다. | |
*/ | |
public async parentDirectory(): Promise<Directory|null> { | |
const folder = await this.drive.files.get( | |
{ | |
fileId: this.id, | |
fields: "parents" | |
}); | |
return (folder.data.parents && typeof folder.data.parents[0] === "string") ? new Directory(this.drive, folder.data.parents[0]) : new Directory(this.drive); | |
} | |
/** | |
* 현재 파일의 이름을 반환합니다. | |
*/ | |
public async getName(): Promise<string> { | |
const file = await this.getInfo("name"); | |
return file.name as string; | |
} | |
/** | |
* 현재 파일의 크기를 반환합니다. | |
*/ | |
public async getSize(): Promise<string> { | |
const file = await this.getInfo("size"); | |
return file.size as string; | |
} | |
/** | |
* 현재 파일의 정보를 반환합니다 | |
* | |
* @param fields 받을 정보를 정합니다 ex.`name`, `size` | |
*/ | |
public async getInfo(fields?: string): Promise<drive_v3.Schema$File> { | |
const file = await this.drive.files.get({ | |
fileId: this.id, | |
fields | |
}); | |
return file.data; | |
} | |
/** | |
* 현재 파일을 로컬로 다운로드 합니다. | |
* | |
* @param fileName 다운로드할 위치 | |
*/ | |
public async download(fileName: string): Promise<void> { | |
// For converting document formats, and for downloading template | |
// documents, see the method drive.files.export(): | |
// https://developers.google.com/drive/api/v3/manage-downloads | |
return this.drive.files | |
.get({fileId: this.id, alt: 'media'}, {responseType: 'stream'}) | |
.then(res => { | |
return new Promise((resolve) => { | |
const dest = fs.createWriteStream(fileName); | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
(res.data as any).on('end', () => { | |
resolve(); | |
}).pipe(dest); | |
}); | |
}); | |
} | |
/** | |
* 현재 파일/폴더를 지정한 디렉토리로 이동합니다 | |
* | |
* @param destinationDirectory 이동할 도착지 디렉토리 | |
*/ | |
public async move(destinationDirectory: Directory): Promise<boolean> { | |
const file = await this.drive.files.get({ | |
fileId: this.id, | |
fields: 'parents' | |
}); | |
const parents = file.data.parents; | |
if (parents === undefined || parents === null) return false; | |
await this.drive.files.update({ | |
fileId: this.id, | |
addParents: destinationDirectory.id, | |
removeParents: parents.join(','), | |
fields: 'id, parents' | |
}); | |
return true; | |
} | |
/** | |
* 파일/폴더를 삭제합니다. | |
* | |
* @returns 현재 파일/폴더의 상위 폴더를 반환합니다. | |
*/ | |
public async delete(): Promise<Directory|null> { | |
const parent = await this.parentDirectory(); | |
await this.drive.files.delete({ | |
fileId: this.id | |
}); | |
return parent; | |
} | |
/** | |
* 파일/폴더의 이름을 변경합니다. 변경후 file의 id 가 갱신되는 경우, 현 오브젝트에 갱신됩니다. | |
* | |
* @returns 성공여부를 반환합니다 (boolean) | |
*/ | |
public async rename(fileName: string): Promise<boolean> { | |
const file = await this.drive.files.update({ | |
fileId: this.id, | |
requestBody: { | |
name: fileName | |
} | |
}); | |
if (typeof file.data.id === "undefined" || file.data.id === null) { | |
return false; | |
} | |
this.id = file.data.id; | |
return true; | |
} | |
} | |
export default File; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment