Skip to content

Instantly share code, notes, and snippets.

@Alex4386
Created October 20, 2020 11:25
Show Gist options
  • Select an option

  • Save Alex4386/1cb6b0dc7bdf35f11046f6d6d8a9b3dd to your computer and use it in GitHub Desktop.

Select an option

Save Alex4386/1cb6b0dc7bdf35f11046f6d6d8a9b3dd to your computer and use it in GitHub Desktop.

Revisions

  1. Alex4386 created this gist Oct 20, 2020.
    331 changes: 331 additions & 0 deletions directory.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,331 @@
    /* 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;
    142 changes: 142 additions & 0 deletions file.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,142 @@
    /* 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;