//tips
//smart contract
Firebaseと連携してimageのアップロードと表示を行うことができるようにする。
まずはNext.jsアプリからFirebase インスタンスにアクセスできるようにしていく。
firebaseにてMyAppプロジェクトを作成。
Next.js を使ったクライアントアプリから Firebase を使うことを想定しているので、ウェブアプリとして登録。
そうすると、下記の表示が現れるのでメモ。これが Next.js アプリ内で FirebaseApp インスタンスを生成するためのコード。
//次のコマンドを実行して最新の SDK をインストール可能
npm install firebase
//Firebase初期化コード
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey:
authDomain:
projectId: "myapp-8e583",
storageBucket: "myapp-8e583.appspot.com",
messagingSenderId:
appId:
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
Next.js アプリから Firebase にアクセスできるようにするため、FirebaseApp インスタンスを初期化するモジュールを作成。
提供されている.tsをjs変換するために
npm install typescript --save-dev
npm install -g typescript
をインストール。
これで実行してもエラーが出たがきちんとtsc init.tsが置かれているフォルダまで移動してあげることで実行できた。
cd utils/firebase
tsc init.ts
ちなみにひとつ前のフォルダに戻るにはcd ../で可能。
ただ、同名のjsファイルが複製できたのはいいが中身は空である。
npm install @types/node --save-dev
TypeScript 用のビルド設定ファイルである tsconfig.json を作成
これで無事にtscコマンドが使用できるようになった。
また、tscを使わないでもCmd/Ctrl + Shift + Bコマンドを行い、tsc: build - tsconfig.jsonを選んでも同様の処理を行うことができる。
https://maku.blog/p/ak7u3h3/#packagejson-%E3%81%AE%E4%BD%9C%E6%88%90
また、コンパイル後に変更前のtsを削除したらエラーが出てしまうので、.tsはそのまま残しておく。
https://stackoverflow.com/questions/41211566/tsconfig-json-buildno-inputs-were-found-in-config-file
これをもとに[id].jsにfirebaseの接続結果を出してみる。
単純に下記のようにしてみたがエラー。
import { getApp, FirebaseApp } from 'firebase/app'
import '../../utils/firebase/init' // Initialize FirebaseApp
const app=()=>{
FirebaseApp = getApp()
}
<p>{ app.name }</p>
<p>{ app.options.appId }</p>
<p>{ app.options.apiKey }</p>
流れは掴めたのでStorageを使っているものを参考にして修正していく。
https://dev.to/itnext/how-to-do-image-upload-with-firebase-in-react-cpj
Storageのruleは下記のように変更しておく。
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if true;
}
}
}
componentフォルダに先のinit.jsの内容を保存。
[id].jsに反映したかったがうまくいかず、もっとシンプルなものから試すべきだったよう。
const storage= firebase.storage()ができず、エラー地獄。バージョンアップでかなりの変化があったからか。
最新のものに対応してそうな記述でトライ。シンプルなものからスタート。
https://www.azukipan.com/posts/firebase-storage-get-preview/
まずはstorage内に画像をアップデートしておき、それをアプリ側で表示できるようにする。
import firebase from 'firebase/app'
import 'firebase/storage'
import { getStorage } from "firebase/storage"
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = {
…
};
// Initialize Firebase
const firebase = initializeApp(firebaseConfig);
const firestorage = getStorage(firebase)
export {
firestorage, firebase as default
}
import { getStorage } from "firebase/storage”を使用して取得。また、ここでappでのexportではなくfirebaseでのexportにしている。
どの画像を参照するかを指定するために、refを使う。firebase/storageからrefをインポート。
gsReference を作成し、第一引数に先程の storage、第二引数に Firebase の Storage に保存した画像の場所を指定。
これは場所を指定しただけなので、これをurlで取得していく。
画像のURLを取得するには、getDownloadURLを使用。
https://firebase.google.com/docs/storage/web/download-files
エラー箇所をドキュメントを見ながら解決していく。下記で無事表示させることができた。
import React, {useState} from 'react'
//import firebase from "../components/firebase"
import firebaseApp from "../components/firebase"
//import { ref } from "firebase/storage"
import { getStorage, ref,getDownloadURL } from "firebase/storage";
function apptest() {
const firestorage = getStorage(firebaseApp);
const [image,setImage]=useState('')
const gsReference = ref(
firestorage,
"gs://myapp-8e583.appspot.com/image.jpg"
)
getDownloadURL(gsReference)
.then(url => {
setImage(url)
})
.catch(err => console.log(err))
return (
<div>
<img src={image} alt="" />
</div>
);
}
export default apptest;
ここまでだと表示させただけなのでアップロードもさせてみる。
画像を送信するために、uploadBytesを使用。
uploadBytes の第一引数にimageRef、第二引数にimageを指定。保存したいファイル名は、image の name を指定。
こちらのエラーが発生。
FirebaseError: Firebase Storage: The operation 'uploadBytes' cannot be performed on a root reference, create a non-root reference using child, such as .child('file.png'). (storage/invalid-root-operation)
これは先のfirebase側からの反映の部分でバッティングしていたためのよう。
import React, {useState} from 'react'
//import firebase from "../components/firebase"
import firebaseApp from "../components/firebase"
//import { ref } from "firebase/storage"
import { getStorage, ref,getDownloadURL,uploadBytes } from "firebase/storage";
function apptest() {
const firestorage = getStorage(firebaseApp);
const [image,setImage]=useState('')
console.log(image)
//image表示
// const gsReference = ref(
// firestorage,
// "gs://myapp-8e583.appspot.com/image.jpg"
// )
// getDownloadURL(gsReference)
// .then(url => {
// setImage(url)
// })
// .catch(err => console.log(err))
const handleChange = e => {
setImage(e.target.files[0])
console.log(image)
}
const handleSubmit = e => {
e.preventDefault()
console.log(image)
try {
const storage = getStorage();
const imageRef = ref(storage, image.name)//firestorage
//const imageRef = storage.ref(image.name)//firestorage
uploadBytes(imageRef, image).then(() => {
console.log("Uploaded a file!")
})
} catch (err) {
console.log(err)
}
}
return (
<div>
<form onSubmit={handleSubmit}>
<input type="file" onChange={handleChange} />
<button className="button">送信</button>
</form>
<img src={image} alt="" />
</div>
);
}
export default apptest;
きちんと解決できた。
最初のデータは初期の読み込みにし、後からの変更を受け付けられるように設定する。下記のようにuseeffextで初回読み込みと後の変更を分けようとしたが、render部分でエラーが発生。変数を突如切り替えるのはダメなよう。
useEffect(() => {
const gsReference = ref(
firestorage,
"gs://myapp-8e583.appspot.com/image.jpg"
)
getDownloadURL(gsReference)
.then(url => {
setImage(url)
})
.catch(err => console.log(err))
}, [])
{image && (
<div>
<p>{image}</p>
</div>
)}
別の設定ページに飛ばすなどした方が無難か。
ひとまず[id].jsのページでimageを表示させることに成功した。
ユーザーごとにgs://myapp-8e583.appspot.comを振り分けるのは容量が大きくなりすぎるだろうか。
gs://myapp-8e583.appspot.comのアイテムの名前でimageを管理するか。
データをユーザごとに保存する方法を確認する。
ユーザーデータベース構成の仕組みと登録追加方法を確認し、ルートディレクトリであるgs://myapp-8e583.appspot.comの中にユーザーごとのid名のフォルダを生成し、その中にアップさせる方法を確認。
https://jpcodeqa.com/q/e1c8d47615aa6dfcb20db9d97727267c
https://coconala.com/blogs/1638666/36569
一旦考えられる動的なフォルダ作成方法で実験。
let path = "gs://[プロジェクト名].appspot.com/test/test.png"
let imageRef = reference.child(path)
これを参考に下記のようにしたらtestフォルダの中に選択したimageを格納することができた。
const path="test/"+image.name
//const imageRef = ref(storage, image.name)//firestorage
const imageRef = ref(storage, path)
このフォルダ箇所をユーザーのidになるようにすれば問題なさそう。editする際には[id]を通るのでこのユーザーidは取得できるようになっているので問題ない。
ここからはjson serverに入れていた内容をfirestorageに移して反映させていく。