もくまっち開発 Week 5
Standard プランに契約したら、 Download Code
とかのメニューが表示されるように
中身はこんな感じ。
たぶん一般的なFlutterプロジェクト何だと思う
作ってるページ発見
Custom Function は内部的には 1 つのファイルにまとめられてるっぽい
ということは別の Custom Function は普通に関数として呼べるかも
ひとまず、 Web アプリとしてビルドしてみる
環境
$ flutter --version
Flutter 3.13.8 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 6c4930c4ac (3 months ago) • 2023-10-18 10:57:55 -0500
Engine • revision 767d8c75e8
Tools • Dart 3.1.4 • DevTools 2.25.0
実行したコマンド
$ flutter build web
Resolving dependencies... (2.9s)
+ _flutterfire_internals 1.3.16
+ async 2.11.0
+ auto_size_text 3.0.0
+ boolean_selector 2.1.1
+ cached_network_image 3.2.1 (3.3.1 available)
+ cached_network_image_platform_interface 1.0.0 (4.0.0 available)
+ cached_network_image_web 1.0.1 (1.1.1 available)
+ characters 1.3.0
+ clock 1.1.1
+ cloud_firestore 4.13.1 (4.14.0 available)
+ cloud_firestore_platform_interface 6.0.5 (6.1.0 available)
+ cloud_firestore_web 3.8.5 (3.9.0 available)
+ collection 1.17.2 (1.18.0 available)
+ crypto 3.0.3
+ cupertino_icons 1.0.6
+ fake_async 1.3.1
+ ffi 2.1.0
+ file 7.0.0
+ firebase_core 2.24.2
+ firebase_core_platform_interface 5.0.0
+ firebase_core_web 2.10.0
+ firebase_performance 0.9.3+8
+ firebase_performance_platform_interface 0.1.4+16
+ firebase_performance_web 0.1.4+16
+ floating_bottom_navigation_bar 1.5.2
+ flutter 0.0.0 from sdk flutter
+ flutter_animate 4.1.1+1 (4.3.0 available)
+ flutter_blurhash 0.7.0 (0.8.2 available)
+ flutter_cache_manager 3.3.1
+ flutter_lints 3.0.0 (3.0.1 available)
+ flutter_localizations 0.0.0 from sdk flutter
+ flutter_test 0.0.0 from sdk flutter
+ flutter_web_plugins 0.0.0 from sdk flutter
+ font_awesome_flutter 10.6.0
+ from_css_color 2.0.0
+ geolocator 10.0.1 (10.1.0 available)
+ geolocator_android 4.2.4 (4.4.0 available)
+ geolocator_apple 2.2.7 (2.3.3 available)
+ geolocator_platform_interface 4.0.8 (4.2.0 available)
+ geolocator_web 2.1.6 (2.2.0 available)
+ geolocator_windows 0.2.0 (0.2.2 available)
+ go_router 7.1.1 (13.0.1 available)
+ google_fonts 4.0.3 (6.1.0 available)
+ http 0.13.6 (1.1.2 available)
+ http_parser 4.0.2
+ intl 0.18.1 (0.19.0 available)
+ iregexp 0.1.1 (0.1.2 available)
+ js 0.6.7 (0.7.0 available)
+ json_path 0.6.2 (0.7.0 available)
+ lints 3.0.0
+ logging 1.2.0
+ matcher 0.12.16 (0.12.16+1 available)
+ material_color_utilities 0.5.0 (0.8.0 available)
+ maybe_just_nothing 0.5.3
+ meta 1.9.1 (1.11.0 available)
+ nested 1.0.0
+ octo_image 1.0.2 (2.0.0 available)
+ page_transition 2.1.0
+ path 1.8.3 (1.9.0 available)
+ path_provider 2.0.14 (2.1.1 available)
+ path_provider_android 2.0.25 (2.2.2 available)
+ path_provider_foundation 2.2.2 (2.3.1 available)
+ path_provider_linux 2.1.11 (2.2.1 available)
+ path_provider_platform_interface 2.0.6 (2.1.1 available)
+ path_provider_windows 2.1.7 (2.2.1 available)
+ petitparser 5.4.0 (6.0.2 available)
+ platform 3.1.4
+ plugin_platform_interface 2.1.6 (2.1.8 available)
+ provider 6.0.5 (6.1.1 available)
+ rfc_6901 0.1.1 (0.2.0 available)
+ rxdart 0.27.7
+ shared_preferences 2.2.2
+ shared_preferences_android 2.2.1
+ shared_preferences_foundation 2.3.4
+ shared_preferences_linux 2.3.2
+ shared_preferences_platform_interface 2.3.1
+ shared_preferences_web 2.2.1 (2.2.2 available)
+ shared_preferences_windows 2.3.2
+ sky_engine 0.0.99 from sdk flutter
+ source_span 1.10.0
+ sqflite 2.2.6 (2.3.0 available)
+ sqflite_common 2.5.0+2
+ stack_trace 1.11.0 (1.11.1 available)
+ stream_channel 2.1.1 (2.1.2 available)
+ stream_transform 2.1.0
+ string_scanner 1.2.0
+ synchronized 3.1.0+1
+ term_glyph 1.2.1
+ test_api 0.6.0 (0.7.0 available)
+ timeago 3.2.2 (3.6.0 available)
+ typed_data 1.3.2
+ url_launcher 6.1.10 (6.2.2 available)
+ url_launcher_android 6.0.27 (6.2.1 available)
+ url_launcher_ios 6.1.4 (6.2.2 available)
+ url_launcher_linux 3.0.6 (3.1.1 available)
+ url_launcher_macos 3.0.7 (3.1.0 available)
+ url_launcher_platform_interface 2.1.2 (2.3.0 available)
+ url_launcher_web 2.1.0 (2.2.3 available)
+ url_launcher_windows 3.0.8 (3.1.1 available)
+ uuid 3.0.7 (4.3.1 available)
+ vector_math 2.1.4
+ web 0.1.4-beta (0.4.0 available)
+ win32 5.1.1 (5.2.0 available)
+ xdg_directories 1.0.4
Changed 104 dependencies!
Upgrading .gitignore
Font asset "CupertinoIcons.ttf" was tree-shaken, reducing it from 283452 to 1272
bytes (99.6% reduction). Tree-shaking can be disabled by providing the
--no-tree-shake-icons flag when building your app.
Font asset "fa-brands-400.ttf" was tree-shaken, reducing it from 189684 to 1532 bytes
(99.2% reduction). Tree-shaking can be disabled by providing the
--no-tree-shake-icons flag when building your app.
Font asset "fa-solid-900.ttf" was tree-shaken, reducing it from 394668 to 1652 bytes
(99.6% reduction). Tree-shaking can be disabled by providing the
--no-tree-shake-icons flag when building your app.
Font asset "MaterialIcons-Regular.otf" was tree-shaken, reducing it from 1645184 to
7680 bytes (99.5% reduction). Tree-shaking can be disabled by providing the
--no-tree-shake-icons flag when building your app.
Compiling lib/main.dart for the Web... 26.1s
成功したっぽい...?
ビルド結果はどこに出力されるんだろ
build/web
配下にいた
Android と iOS のビルドは下記コマンドっぽい
$ flutter build apk
$ flutter build ios
$ flutter build -h
Available subcommands:
aar Build a repository containing an AAR and a POM file.
apk Build an Android APK file from your app.
appbundle Build an Android App Bundle file from your app.
bundle Build the Flutter assets directory from your app.
ios Build an iOS application bundle (macOS host only).
ios-framework Produces .xcframeworks for a Flutter project and its plugins for integration into existing, plain iOS Xcode projects.
ipa Build an iOS archive bundle and IPA for distribution (macOS host only).
macos Build a macOS desktop application.
macos-framework Produces .xcframeworks for a Flutter project and its plugins for integration into existing, plain macOS Xcode projects.
web Build a web application bundle.
Android のビルドをしてみる
$ flutter build apk
Android SDK がないと怒られるのでインストールする
[!] No Android SDK found. Try setting the ANDROID_SDK_ROOT environment variable.
Docker で開発環境を用意するチャレンジ
Docker に手こずりまくったので、一旦 Mac ローカルで環境構築
- Android Studio インストール & PATH を通す(
.zshrc
に下記を追記)
export ANDROID_HOME=/Users/{ユーザー名}/Library/Android/sdk
export ANDROID_SDK_ROOT=/Users/{ユーザー名}/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/tools:ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools
- Flutter/Dart は Nix で用意
nix-shell -p flutter dart
apk ファイルにビルドしてみる
flutter build apk
Upgrading build.gradle
Checking the license for package Android SDK Tools in /Users/nix/Library/Android/sdk/licenses
License for package Android SDK Tools accepted.
Preparing "Install Android SDK Tools (revision: 26.1.1)".
"Install Android SDK Tools (revision: 26.1.1)" ready.
Installing Android SDK Tools in /Users/nix/Library/Android/sdk/tools
"Install Android SDK Tools (revision: 26.1.1)" complete.
"Install Android SDK Tools (revision: 26.1.1)" finished.
Checking the license for package Android SDK Build-Tools 30.0.3 in /Users/nix/Library/Android/sdk/licenses
License for package Android SDK Build-Tools 30.0.3 accepted.
Preparing "Install Android SDK Build-Tools 30.0.3 (revision: 30.0.3)".
"Install Android SDK Build-Tools 30.0.3 (revision: 30.0.3)" ready.
Installing Android SDK Build-Tools 30.0.3 in /Users/nix/Library/Android/sdk/build-tools/30.0.3
"Install Android SDK Build-Tools 30.0.3 (revision: 30.0.3)" complete.
"Install Android SDK Build-Tools 30.0.3 (revision: 30.0.3)" finished.
Checking the license for package Android SDK Platform 33 in /Users/nix/Library/Android/sdk/licenses
License for package Android SDK Platform 33 accepted.
Preparing "Install Android SDK Platform 33 (revision: 3)".
"Install Android SDK Platform 33 (revision: 3)" ready.
Installing Android SDK Platform 33 in /Users/nix/Library/Android/sdk/platforms/android-33
"Install Android SDK Platform 33 (revision: 3)" complete.
"Install Android SDK Platform 33 (revision: 3)" finished.
Checking the license for package Android SDK Platform 31 in /Users/nix/Library/Android/sdk/licenses
License for package Android SDK Platform 31 accepted.
Preparing "Install Android SDK Platform 31 (revision: 1)".
"Install Android SDK Platform 31 (revision: 1)" ready.
Installing Android SDK Platform 31 in /Users/nix/Library/Android/sdk/platforms/android-31
"Install Android SDK Platform 31 (revision: 1)" complete.
"Install Android SDK Platform 31 (revision: 1)" finished.
Font asset "fa-solid-900.ttf" was tree-shaken, reducing it from 394668 to 1652 bytes (99.6% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
Font asset "CupertinoIcons.ttf" was tree-shaken, reducing it from 283452 to 912 bytes (99.7% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
Font asset "MaterialIcons-Regular.otf" was tree-shaken, reducing it from 1645184 to 1468 bytes (99.9% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
Font asset "fa-brands-400.ttf" was tree-shaken, reducing it from 189684 to 1532 bytes (99.2% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
注意:/Users/nix/.pub-cache/hosted/pub.dev/cloud_firestore-4.13.1/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestorePlugin.javaは推奨されないAPIを使用またはオーバーライドしています。
注意:詳細は、-Xlint:deprecationオプションを指定して再コンパイルしてください。
注意:入力ファイルの操作のうち、未チェックまたは安全ではないものがあります。
注意:詳細は、-Xlint:uncheckedオプションを指定して再コンパイルしてください。
注意:/Users/nix/.pub-cache/hosted/pub.dev/geolocator_android-4.2.4/android/src/main/java/com/baseflow/geolocator/location/LocationManagerClient.javaは推奨されないAPIを使用またはオーバーライドしています。
注意:詳細は、-Xlint:deprecationオプションを指定して再コンパイルしてください。
Running Gradle task 'assembleRelease'... 228.7s
✓ Built build/app/outputs/flutter-apk/app-release.apk (23.8MB).
注意って出てるけど、ビルドできたっぽい
次は iOS にチャレンジ
flutter build ios
Application not configured for iOS
make: *** [Makefile:7: build-ios] Error 1
あえなく失敗。設定されてないらしい。
Web はもともとできてたけど、改めてできることを確認
flutter build web
Font asset "fa-solid-900.ttf" was tree-shaken, reducing it from 394668 to 1652 bytes (99.6% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
Font asset "CupertinoIcons.ttf" was tree-shaken, reducing it from 283452 to 1272 bytes (99.6% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
Font asset "fa-brands-400.ttf" was tree-shaken, reducing it from 189684 to 1532 bytes (99.2% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
Font asset "MaterialIcons-Regular.otf" was tree-shaken, reducing it from 1645184 to 7680 bytes (99.5% reduction). Tree-shaking can be disabled by providing the --no-tree-shake-icons flag when building your app.
Compiling lib/main.dart for the Web... 1,985ms
apk ファイルを Pixel 6a にインストールして動かしてみた
Flutter のビルド状況
- Android
- iOS
- Web
iOS は来週
あと GitHub Actions で自動化したい
ここから Google Drive のシーシャ画像を Google Drive API を使って取得していく
クイックスタートを上から実行する
OAuth の設定がちょっとめんどくさかったけど、上から順番にやったら普通に動いた :)
TypeScript のサンプルコードもほしい
クイックスタートのコードをちょっと変更
-
.js
→.ts
- 型エラーになるところを無視(直したい)
-
getInstance
,listFile
,getFile
を追加
import { GoogleApis, drive_v3 } from "googleapis";
const fs = require('fs').promises;
const path = require('path');
const process = require('process');
const { authenticate } = require('@google-cloud/local-auth');
const { google } = require('googleapis');
/**
* @var {drive_v3.Drive}
*/
let instance: any = null;
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = path.join(process.cwd(), 'token.json');
const CREDENTIALS_PATH = path.join(process.cwd(), 'credentials.json');
/**
* Reads previously authorized credentials from the save file.
*
* @return {Promise<OAuth2Client|null>}
*/
async function loadSavedCredentialsIfExist() {
try {
const content = await fs.readFile(TOKEN_PATH);
const credentials = JSON.parse(content);
return google.auth.fromJSON(credentials);
} catch (err) {
return null;
}
}
/**
* Serializes credentials to a file comptible with GoogleAUth.fromJSON.
*
* @param {OAuth2Client} client
* @return {Promise<void>}
*/
// FIXME: 型
// @ts-ignore
async function saveCredentials(client) {
const content = await fs.readFile(CREDENTIALS_PATH);
const keys = JSON.parse(content);
const key = keys.installed || keys.web;
const payload = JSON.stringify({
type: 'authorized_user',
client_id: key.client_id,
client_secret: key.client_secret,
refresh_token: client.credentials.refresh_token,
});
await fs.writeFile(TOKEN_PATH, payload);
}
/**
* Load or request or authorization to call APIs.
*
*/
async function authorize() {
let client = await loadSavedCredentialsIfExist();
if (client) {
return client;
}
client = await authenticate({
scopes: SCOPES,
keyfilePath: CREDENTIALS_PATH,
});
if (client.credentials) {
await saveCredentials(client);
}
return client;
}
const getInstance = async (): Promise<drive_v3.Drive> => {
// FIXME: 型
// @ts-ignore
if (!instance) {
const client = await authorize();
instance = google.drive({ version: 'v3', auth: client });
}
return instance;
}
export async function listFiles() {
const drive = await getInstance();
const res = await drive.files.list({
pageSize: 10,
fields: 'nextPageToken, files(id, name)',
});
const files = res.data.files;
if (!files || files.length === 0) {
console.log('No files found.');
return;
}
console.log('Files:');
files.map((file) => {
console.log(`${file.name} (${file.id})`);
});
return files;
}
export async function getFile(id: string) {
const drive = await getInstance();
const file = await drive.files.get({
fileId: id,
alt: 'media'
});
console.log(file.data.name);
}
index.ts
から上記のlistFiles
を呼び出すとマイドライブのファイルリストが取得できた
bun run src/index.ts
Files:
work-room-2022 (14wV1VeFeCVcUZDQybJDnNxxxxxxxxxxxxxxx)
bed-room-2022 (1udSd3LuaR9MYU1k2kK05KeSrhO5xxxxxxxxxxxxx)
為替、VOO、VTI (1XyJ4k_JnN5bplGlwbVBxa2Jj5rNO2xxxxxxxxxxxxxxx)
UkkaReminder-release.apk (1UoFdSIqDQuT9LuyRCpxxxxxxxxxxxxxxx)
1847C181-3669-4FBC-AEC3-413DC63EA203.png (1B51xvgluhXOX7QE_0xxxxxxxxxxxxx)
もくまっち (1vWiqHwgf5V0RXK8nsxxxxxxxxxxxxxxxxxxx)
app-release.apk.sha1 (1JdU1nYnJhy5dBqp_sYD8Wxxxxxxxxxx)
app-release.apk (1KlCeUM33hOg5Hx8e6E616xxxxxxxxxxx)
もくまっち(回答) のコピー (1Ffvt2OiVrDpScJrFUmjWByuBwMqs7x0-xxxxxxxxxx)
mochmatch (1RdRM5ugCDudY6PD9pm_k4Uxxxxxxxxxx)
getFile
も呼んでみるが、エラーになる。
どうやら権限がない様子。
14 | Object.defineProperty(exports, "__esModule", { value: true });
15 | exports.GaxiosError = void 0;
16 | /* eslint-disable @typescript-eslint/no-explicit-any */
17 | class GaxiosError extends Error {
18 | constructor(message, options, response) {
19 | super(message);
^
error: The user has not granted the app 334958xxxxxx read access to the file 1UoFdSIqDQuT9LuyRCpB1mIKxxxxxxxxxx.
errors: [
{
"message": "The user has not granted the app 3349583xxxxx read access to the file 1UoFdSIqDQuT9LuyRCpB1mIxxxxxxxxxx.",
"domain": "global",
"reason": "appNotAuthorizedToFile",
"location": "Authorization",
"locationType": "header"
}
]
code: "403"
これっぽい
appNotAuthorizedToFile
このエラーは、アプリの ACL にアプリがない場合に発生します。このエラーにより、ユーザーはアプリでファイルを開くことができなくなります。
スコープを下記に変更したら解消した
https://www.googleapis.com/auth/drive.readonly
import { GoogleApis, drive_v3 } from "googleapis";
const fs = require('fs').promises;
const path = require('path');
const process = require('process');
const { authenticate } = require('@google-cloud/local-auth');
const { google } = require('googleapis');
/**
* @var {drive_v3.Drive}
*/
let instance: any = null;
// If modifying these scopes, delete token.json.
const SCOPES = [
- 'https://www.googleapis.com/auth/drive.metadata.readonly'
+ 'https://www.googleapis.com/auth/drive.readonly',
];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = path.join(process.cwd(), 'token.json');
const CREDENTIALS_PATH = path.join(process.cwd(), 'credentials.json');
/**
* Reads previously authorized credentials from the save file.
*
* @return {Promise<OAuth2Client|null>}
*/
async function loadSavedCredentialsIfExist() {
try {
const content = await fs.readFile(TOKEN_PATH);
const credentials = JSON.parse(content);
return google.auth.fromJSON(credentials);
} catch (err) {
return null;
}
}
/**
* Serializes credentials to a file comptible with GoogleAUth.fromJSON.
*
* @param {OAuth2Client} client
* @return {Promise<void>}
*/
// FIXME: 型
// @ts-ignore
async function saveCredentials(client) {
const content = await fs.readFile(CREDENTIALS_PATH);
const keys = JSON.parse(content);
const key = keys.installed || keys.web;
const payload = JSON.stringify({
type: 'authorized_user',
client_id: key.client_id,
client_secret: key.client_secret,
refresh_token: client.credentials.refresh_token,
});
await fs.writeFile(TOKEN_PATH, payload);
}
/**
* Load or request or authorization to call APIs.
*
*/
async function authorize() {
let client = await loadSavedCredentialsIfExist();
if (client) {
return client;
}
client = await authenticate({
scopes: SCOPES,
keyfilePath: CREDENTIALS_PATH,
});
if (client.credentials) {
await saveCredentials(client);
}
return client;
}
const getInstance = async (): Promise<drive_v3.Drive> => {
// FIXME: 型
// @ts-ignore
if (!instance) {
const client = await authorize();
instance = google.drive({ version: 'v3', auth: client });
}
return instance;
}
export async function listFiles() {
const drive = await getInstance();
const res = await drive.files.list({
pageSize: 10,
fields: 'nextPageToken, files(id, name)',
});
const files = res.data.files;
if (!files || files.length === 0) {
console.log('No files found.');
return;
}
console.log('Files:');
files.map((file) => {
console.log(`${file.name} (${file.id})`);
});
return files;
}
export async function getFile(id: string) {
const drive = await getInstance();
const file = await drive.files.get({
fileId: id,
alt: 'media'
});
console.log(file.data.name);
}
getFile
を一部変更
export async function getFile(id: string) {
const drive = await getInstance();
- const file = await drive.files.get({
+ const {
+ config,
+ data,
+ headers,
+ request,
+ status,
+ statusText
+ } = await drive.files.get({
+ fileId: id,
+ });
- console.log(file.data.name);
+ console.debug({
+ config,
+ data,
+ headers,
+ request,
+ status,
+ statusText
+ });
}
リクエストとレスポンスはこんな感じ
ファイルのメタデータしかないけど、どうやってダウンロードするんだろ
{
config: {
url: "https://www.googleapis.com/drive/v3/files/1a2IKptsGtn7gqQJrKI2udtv8zBPJVyhW",
method: "GET",
userAgentDirectives: [
[Object ...]
],
paramsSerializer: [Function],
headers: {
"x-goog-api-client": "gdcl/6.0.4 gl-node/20.8.0",
"Accept-Encoding": "gzip",
"User-Agent": "google-api-nodejs-client/6.0.4 (gzip)",
Authorization: "Bearer ya29.a0AfB_byCWNTfDdu3htMaIXGEsn6S7A5s9MT-3hBq332eVOMkh8L4WZdyFKoTMjGFV9mYNYh8owZflRG5ryDeje8zyKKql3hUviiCwD_PuvX3bf9qjEwdJU00qdzzVS9rKpRXCNuKGChEiA6WPVtak-DZFUNNtrX8iQ5B1aCgYKARoSARMSFQHGX2MiOpzgX9hgnrKxKxqCbzED3g0171",
Accept: "application/json",
},
params: {},
validateStatus: [Function],
retry: true,
responseType: "json",
},
data: {
kind: "drive#file",
id: "1a2IKptsGtn7gqQJrKI2udtv8zBPJVyhW",
name: "1Gwj2a77NTcjmUR79eg-aAqwlEqfld5mY.webp",
mimeType: "image/webp",
},
headers: {
"alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
"cache-control": "no-cache, no-store, max-age=0, must-revalidate",
"content-encoding": "gzip",
"content-type": "application/json; charset=UTF-8",
date: "Sat, 13 Jan 2024 08:29:19 GMT",
expires: "Mon, 01 Jan 1990 00:00:00 GMT",
pragma: "no-cache",
server: "ESF",
"transfer-encoding": "chunked",
vary: "Origin, X-Origin",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"x-xss-protection": "0",
},
request: {
responseURL: "https://www.googleapis.com/drive/v3/files/1a2IKptsGtn7gqQJrKI2udtv8zBPJVyhW",
},
status: 200,
statusText: "OK",
}
リクエストにalt=media
をつけないといけないっぽい
ドライブに保存されている blob ファイルをダウンロードするには、ダウンロードするファイルの ID と alt=media URL パラメータを指定して files.get メソッドを使用します。alt=media URL パラメータは、コンテンツのダウンロードが代替レスポンス形式としてリクエストされていることをサーバーに伝えます。
alt=media
を追加
export async function getFile(id: string) {
const drive = await getInstance();
const {
config,
data,
headers,
request,
status,
statusText
} = await drive.files.get({
fileId: id,
+ alt: 'media',
});
console.debug({
config,
data,
headers,
request,
status,
statusText
});
}
レスポンスボディがバイナリになった!
drive.readonly.metadata スコープを使用するアプリには、ファイルの内容をダウンロードする権限がありません。
なるほど。だから最初ダウンロードできなかったのか。
いや、files.get
じゃなくてfiles.export
を使うべきな気がしてきた
違った。 Google ドキュメントファイル限定っぽい。
Google Workspace ドキュメントをリクエストされた MIME タイプにエクスポートし、エクスポートされたバイト コンテンツを返します。エクスポートできるコンテンツのサイズの上限は 10 MB です。
実行したら↓のエラーが発生
error: {
"error": {
"code": 403,
"message": "Export only supports Docs Editors files.",
"errors": [
{
"message": "Export only supports Docs Editors files.",
"domain": "global",
"reason": "fileNotExportable"
}
]
}
}
export async function exportFile(id: string, mimeType: 'application/pdf') {
const drive = await getInstance();
const stream = await drive.files.export({
fileId: id,
mimeType,
}, {
responseType: "stream",
});
return stream.data;
}
ほしかった情報はここにありそう
ドライブに保存されている blob ファイルをダウンロードするには、ダウンロードするファイルの ID と
alt=media
URL パラメータを指定して files.get メソッドを使用します。
ということで、getFile
を以下のように書き換え
export async function getFile(id: string) {
const drive = await getInstance();
const stream = await drive.files.get(
{
fileId: id,
},
{
responseType: 'stream',
}
);
return {
status: stream.status,
readable: stream.data,
};
}
呼び出し側は下記
import { createWriteStream} from 'fs';
import { getFile } from 'getFile を含むファイルのパス';
const { readable, status } = await getFile('ダウンロードしたいファイルのID'); // listFiles などで取得
const writable = createWriteStream(`./download.webp`);
readable.pipe(writable);
おぉ、ダウンロードできたっぽい
あれ、でもファイルが 0 バイトだ
やっと、、マイドライブからダウンロードできた
共有アイテムもダウンロードできた!
Firestore の更新までできた!
一応同じ写真がアップロードされてるかは別途確認しよう
#もくまっち 開発 Week 6 のタスク
- 写真の before/after 確認
- 写真が横になってるのを縦にする
- HEIF フォーマットの写真のアップロード
- iOS のビルド
- Play ストアの開発者アカウント開設
来週も頑張る!
#ミリコンバレー
今週の進捗
-
Firebase Storage に画像アップロードの残り
- 残り 1 枚まで来た
- 合ってるか確認
- 使い方ページの見た目更新
-
FlutterFlow のコードをダウンロードしてビルドしてみる ←追加
- Android, Web はできた
- iOS はまだ
どれも微妙におわってない