Google DriveAPIで心が折れた 指定したパスのファイルが存在するか知りたいだけだったが、Google Drive Api V3のリファレンスを読んでもあまりファイルパスに関する記述が殆ど見当たらない。 クラウドストレージサービスのリファレンスでそんなことあるかと思って調べた。
Try to stop thinking in terms of file paths. Google Drive is a flat filesystem, where "parent" is simply an attribute, a bit like a tag. A file can have many parents, and so could be on many paths.
Google Driveはフラットファイルシステムなので、ファイルパスで考えるのはやめましょう。"親フォルダ"は単なるタグのような属性です。ファイルは複数の親フォルダを持つ可能性がありますし、複数のパスを持つ可能性があります。
Flat Filesystemって何や...って調べてみると英語版ウィキペディアの項目に小さくFlat Filesystemという項目がある。 単純に階層構造の無いファイルシステムのようだけど何かしらのメリットがあるのだろうか。 Amazon S3も同様の仕組みって書いてあるので大規模クラウドストレージ特有の問題に対応しやすいということだろうか。
要するに指定したパスにファイルが存在するかどうか確認したい場合には再帰的にパスをたどっていくしか無さそう。
ファイルのリストを取得するためにはlistエンドポイントを使用する。 パスで指定するのではなく、qとfiledsパラメータを駆使して全てのファイルの中から必要なデータをフィルターするイメージで考えればよさそう。
qパラメータ
取得したファイル一覧をフィルタリングするためのクエリです。サポートしている文法を確認するためには"Search for files"を確認してください。
fieldsパラメータ
レスポンスに含まれるパスとフィールドを指定します。指定されなければレスポンスはこのメソッドのデフォルトの集合を返します。開発の際にはすべてのフィールドを返す記号*を使えますが、必要なフィールドだけを指定した方がパフォーマンスが優れています。
qで取得するファイルをフィルターして、fieldsで取得する項目(ファイル名、更新日等々)の情報を絞り込む。
javascriptで指定したディレクトリのファイル一覧を取得する。予め何らかの方法でGoogleのOauth2のアクセストークンを取得しておく必要がある。 __listDirは補助関数で本体はlistDir関数となっている。
const axios = require('axios');
const API_KEY = '---- Your API Key -----';
const MIMETYPE_FOLDER = 'application/vnd.google-apps.folder'
/\*\*
\* GoogleDriveからファイルのリストを取得する(補助関数)
\*
\* @param {String} token
\* @param {String} parent - 上位フォルダのID
\* @returns {Object} Google Drive APIからのレスポンス
\*/
async function __listFiles(token, parentId) {
const url = 'https://www.googleapis.com/drive/v3/files';
const options = {
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
params: {
key: API_KEY,
fields: 'files(id, name, mimeType)',
q: `"${parentId}" in parents`,
},
};
let resp = null;
try {
resp = await axios.get(url, options);
return resp.data.files;
} catch (error) {
throw error;
};
}
/\*\*
\* GoogleDriveからファイルのリストを取得する
\*
\* @param {String} token - OAuth2トークン
\* @param {String} path - ディレクトリのパス
\* @returns {Object} 指定したディレクトリ内のファイル一覧
\*/
async function listFiles(token, path) {
const sepPath = path.split('/');
let files = null;
try {
files = await __listFiles(token, 'root');
} catch(error) {
throw error;
}
for (let i=0; i < sepPath.length; i++) {
let nextId = null;
for (let e of files) {
if (sepPath[i] === e.name && e.mimeType === MIMETYPE_FOLDER) {
nextId = e.id;
break;
}
}
if (nextId) {
try {
files = await await __listFiles(token, nextId);
} catch (error) {
throw error;
}
}
}
return files;
}
一個のファイルの情報を確認するためだけに数回のリクエストが必要になる。可能な限り過去のリクエストをキャッシュして通信回数を減らすのがいいのか。