2021/07/19(修改內容)
2021/12/01 (更新內容)
Firebase提供一個可以上傳、下載檔案的Firebase Cloud Storage服務,類似的服務很多,操作方式類似,我們就以Firebase Cloud Storage為例。
上傳到firebase cloud storage
利用檔名上傳到storage:
import { getStorage, ref } from "firebase/storage";
// Create a root reference
const storage = getStorage();
// Create a reference to 'mountains.jpg'
const mountainsRef = ref(storage, 'mountains.jpg');
// 'file' comes from the Blob or File API
uploadBytes(maintainsRef, file).then((snapshot) => {
console.log('Uploaded a blob or file!');
});
import { getStorage, ref, listAll } from "firebase/storage";
const storage = getStorage();
// Create a reference under which you want to list
const listRef = ref(storage, 'files/uid');
// Find all the prefixes and items.
listAll(listRef)
.then((res) => {
res.prefixes.forEach((folderRef) => {
// All the prefixes under listRef.
// You may call listAll() recursively on them.
});
res.items.forEach((itemRef) => {
// All the items under listRef.
});
}).catch((error) => {
// Uh-oh, an error occurred!
});
import { getStorage, ref, getDownloadURL } from "firebase/storage";
// Create a reference to the file we want to download
const storage = getStorage();
const starsRef = ref(storage, 'images/stars.jpg');
// Get the download URL
getDownloadURL(starsRef)
.then((url) => {
// Insert url into an <img> tag to "download"
})
.catch((error) => {
// A full list of error codes is available at
// https://firebase.google.com/docs/storage/web/handle-errors
switch (error.code) {
case 'storage/object-not-found':
// File doesn't exist
break;
case 'storage/unauthorized':
// User doesn't have permission to access the object
break;
case 'storage/canceled':
// User canceled the upload
break;
// ...
case 'storage/unknown':
// Unknown error occurred, inspect the server response
break;
}
});
import { getStorage, ref, deleteObject } from "firebase/storage";
const storage = getStorage();
// Create a reference to the file to delete
const desertRef = ref(storage, 'images/desert.jpg');
// Delete the file
deleteObject(desertRef).then(() => {
// File deleted successfully
}).catch((error) => {
// Uh-oh, an error occurred!
});
首先,先設定Firebase Cloud Storage。建議先將存取規則設定為:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read: if true;
allow write: if request.auth != null;
}
}
}
這樣可以讓所有人可以讀取檔案,但是,要更動(如:上傳檔案)就必須先登入。
進入console
設定存取規則
設定位置
接下來,要設計一個使用者介面,讓使用者透過瀏覽器上傳檔案,可以直接利用Input選擇檔案。
<Input type="file" accept="image/x-png,image/jpeg" onChange={handleUpload}/>
e.target.files裡可以取得Input所選擇的檔案,當只有選擇一個檔案時,就取陣列的第一筆。
console.log(e.target.files[0]);
/src/ui/ImageUpload.js
import React from 'react';
import {Box, Input} from '@mui/material';
export default function ImageUpload() {
const handleUpload = function(e){
console.log(e.target.files[0]);
}
return (
<Box>
<Input type="file" accept="image/x-png,image/jpeg" onChange={handleUpload}/>
</Box>
)
}
應該會在console裡看到類似這樣的內容
File {name: 'cat.jpeg', lastModified: 1576031351526, lastModifiedDate: Wed Dec 11 2019 10:29:11 GMT+0800 (台北標準時間), webkitRelativePath: '', size: 38095, …}
接下來,要把檔案上傳到firebase cloud storage,firebase的範例都是使用.then(),以下都改用以async / await。
再利用原始檔名上傳到storage:
import { getStorage, ref } from "firebase/storage";
// Create a root reference
const storage = getStorage();
// Create a reference to the image
const imageRef = ref(storage,e.target.files[0].name);
// 'file' comes from the Blob or File API
uploadBytes(storageRef, file).then((snapshot) => {
console.log('Uploaded a blob or file!');
});
取得下載的URL
const url = await getDownloadURL(imageRef);
會得到類似這樣的內容:
https://firebasestorage.googleapis.com/v0/b/product-a87b2.appspot.com/o/deer.jpg?alt=media&token=bab5064c-222b-42e2-80d7-10fc5f838f60
/src/ui/ImageUpload.js
import React, {useState} from 'react';
import { getStorage, getDownloadURL, ref, uploadBytes } from "firebase/storage";
import {Box, Input} from '@mui/material';
export default function ImageUpload() {
const storage = getStorage();
const [message, setMessage] = useState("");
const handleUpload = async function(e){
console.log(e.target.files[0]);
try{
setMessage("");
// Create a reference to the image
const imageRef = ref(storage,e.target.files[0].name);
await uploadBytes(imageRef, e.target.files[0]);
console.log('Uploaded a blob or file!');
const url = await getDownloadURL(imageRef);
console.log(url);
}
catch(error){
console.log(error.code);
if (error.code === "storage/unauthorized"){
setMessage("尚未登入");
}
}
}
return (
<Box>
<Input type="file" accept="image/x-png,image/jpeg" onChange={handleUpload}/>
<br/>{message}
</Box>
)
}
useEffect(()=>{
async function readImage() {
try {
setMessage("waiting...");
const listRef = ref(storage, '/');
const result = await listAll(listRef);
setImages([]);
result.items.forEach(async (image) => {
let url = await getDownloadURL(image);
setImages((currentImages)=>[...currentImages,{img:url, title:image.name}]);
});
setMessage("");
}
catch(error){
setMessage(error);
console.log(error);
}
}
readImage();
},[storage]);
return (
<Box>
<Input type="file" accept="image/x-png,image/jpeg" onChange={handleUpload}/>
<br/>{message}
<ImageList sx={{ width: '100%', height: '100%' }} cols={2} rowHeight={164}>
{images.map((item) => (
<ImageListItem key={item.title}>
<img
src={`${item.img}?w=164&h=164&fit=crop&auto=format`}
srcSet={`${item.img}?w=164&h=164&fit=crop&auto=format&dpr=2 2x`}
alt={item.title}
loading="lazy"
/>
</ImageListItem>
))}
</ImageList>
</Box>
)
完整程式
import React, {useState, useEffect} from 'react';
import { getStorage, getDownloadURL, ref, uploadBytes } from "firebase/storage";
import { listAll } from "firebase/storage";
import {Box, Input} from '@mui/material';
import {ImageList, ImageListItem} from '@mui/material';
export default function ImageUpload() {
const storage = getStorage();
const [message, setMessage] = useState("");
const [images, setImages] = useState([]);
const [loaded, setLoaded] = useState(0); //add 1 when loaded
const handleUpload = async function(e){
console.log(e.target.files[0]);
try{
setMessage("");
// Create a reference to the image
const imageRef = ref(storage,e.target.files[0].name);
await uploadBytes(imageRef, e.target.files[0]);
console.log('Uploaded a blob or file!');
const url = await getDownloadURL(imageRef);
console.log(url);
setLoaded((currentValue)=>currentValue+1);
}
catch(error){
console.log(error.code);
if (error.code === "storage/unauthorized"){
setMessage("尚未登入");
}
}
}
useEffect(()=>{
async function readImage() {
try {
setMessage("waiting...");
const listRef = ref(storage, '/');
const result = await listAll(listRef);
setImages([]);
result.items.forEach(async (image) => {
let url = await getDownloadURL(image);
setImages((currentImages)=>[...currentImages,{img:url, title:image.name}]);
});
setMessage("");
}
catch(error){
setMessage(error);
console.log(error);
}
}
readImage();
},[storage, loaded]);
return (
<Box>
<Input type="file" accept="image/x-png,image/jpeg" onChange={handleUpload}/>
<br/>{message}
<ImageList sx={{ width: '100%', height: '100%' }} cols={2} rowHeight={164}>
{images.map((item) => (
<ImageListItem key={item.title}>
<img
src={`${item.img}?w=164&h=164&fit=crop&auto=format`}
srcSet={`${item.img}?w=164&h=164&fit=crop&auto=format&dpr=2 2x`}
alt={item.title}
loading="lazy"
/>
</ImageListItem>
))}
</ImageList>
</Box>
)
}
將url儲存firestore。接下來就可以針對產品(或其他)的照片進行新增、刪除、修改。