Storage
Firebase Cloud Storage
2020/09/04
2020/11/21(補充內容)
2020/11/30(修改內容)
簡介
Firebase提供一個可以上傳、下載檔案的Firebase Cloud Storage服務,類似的服務很多,操作方式類似,我們就以Firebase Cloud Storage為例。
設定
首先,先設定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;
}
}
}
這樣可以讓所有人可以讀取檔案,但是,要更動(如:上傳檔案)就必須先登入。
![](https://www.google.com/images/icons/product/drive-32.png)
進入console
設定存取規則
設定位置
開啟相簿
接下來,要設計一個使用者介面,讓使用者上傳檔案,expo提供了兩個工具:DocumentPicker及ImagePicker,DocumentPicker比較簡單,就是選擇手機中的檔案,ImagePicker除了可以選擇手機中的圖片、影片,還可以存取鏡頭。
首先,安裝套件:
expo install expo-image-picker
根據expo的範例程式,我們稍微修改一下:
/src/storage/ImageUpload.js
import React from 'react';
import { Image, Button, View } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import styles from '../styles'
export default function ImageUpload() {
let openImagePickerAsync = async () => {
let permissionResult = await ImagePicker.requestCameraRollPermissionsAsync();
if (permissionResult.granted === false) {
alert("Permission to access camera roll is required!");
return;
}
let pickerResult = await ImagePicker.launchImageLibraryAsync();
console.log(pickerResult);
}
return (
<View style={styles.container}>
<Image source={{ uri: 'https://i.imgur.com/TkIrScD.png' }} style={styles.logo} />
<Button onPress={openImagePickerAsync} title='選擇檔案'/>
</View>
);
}
修改一下
/src/styles.js
import {StatusBar, StyleSheet} from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
display: "flex",
flexDirection: "column",
justifyContent: "center",
padding: 35,
backgroundColor: '#fff',
//backgroundColor: '#00bfff',
//flex: 1,
//display: 'flex',
//margin: 'auto',
//flexDirection: 'row',
marginTop: StatusBar.currentHeight || 0,
},
item: {
flex: 1,
flexDirection: 'row',
backgroundColor: '#00ffff',
padding: 8,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 24,
},
inputStyle: {
width: '100%',
marginBottom: 15,
paddingBottom: 15,
alignSelf: "center",
borderColor: "#ccc",
borderBottomWidth: 1
},
logo: {
width: 305,
height: 159,
marginBottom: 20,
},
});
export default styles;
App.js
import React, {useState} from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer, StackActions } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createStackNavigator } from '@react-navigation/stack';
import ImageUpload from './src/storage/ImageUpload';
import PersonList from './src/person/PersonList';
import ProductList from './src/product/ProductList';
import SignIn from './src/account/SignIn';
import SignOut from './src/account/SignOut';
import SignUp from './src/account/SignUp';
//import Click from './Click';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
function Home(){
return(
<Tab.Navigator>
<Tab.Screen name="Person" component={PersonList} />
<Tab.Screen name="Product" component={ProductList} />
<Tab.Screen name="Image" component={ImageUpload}/>
</Tab.Navigator>
);
}
function App() {
const [count, setCount] = useState(10);
let countString = "count in App:"+count;
function updateCount(newCount){
setCount(newCount);
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="SignIn" component={SignIn} />
<Stack.Screen name="SignUp" component={SignUp} />
<Stack.Screen name="SignOut" component={SignOut} />
<Stack.Screen name="Home" component={Home} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
接下來,我們要顯示選擇到的照片:
/src/storage/ImageUpload.js
import React, {useState} from 'react';
import { Image, Button, View} from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import styles from '../styles'
export default function ImageUpload() {
const [selectedImage, setSelectedImage] =
useState( {localUri:'https://i.imgur.com/TkIrScD.png'});
let openImagePickerAsync = async () => {
let permissionResult = await ImagePicker.requestCameraRollPermissionsAsync();
if (permissionResult.granted === false) {
alert("Permission to access camera roll is required!");
return;
}
let pickerResult = await ImagePicker.launchImageLibraryAsync();
console.log(pickerResult);
if (!pickerResult.cancelled) {
//if not cancelled
setSelectedImage({ localUri: pickerResult.uri });
}
}
return (
<View style={styles.container}>
<Image source={{ uri: selectedImage.localUri }} style={styles.logo} />
<Button onPress={openImagePickerAsync} title='選擇檔案'/>
</View>
);
}
Firebase Cloud Storage
上傳到firebase cloud storage
如果要利用原始的檔案名稱,可以先利用split取的檔名:
const filename = uri.split('/').pop();
再利用原始檔名上傳到storage:
const ref = firebase.storage().ref().child(filename);
/src/storage/ImageUpload.js
import React, {useState} from 'react';
import { Image, Button, Text, View, YellowBox } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import * as firebase from 'firebase';
import * as FirebaseCore from 'expo-firebase-core';
import styles from '../styles'
export default function ImageUpload() {
YellowBox.ignoreWarnings(['Setting a timer']);
const [selectedImage, setSelectedImage] =
React.useState({localUri:'https://i.imgur.com/TkIrScD.png'});
const [message, setMessage] = useState("");
if (!firebase.apps.length) {
firebase.initializeApp(FirebaseCore.DEFAULT_WEB_APP_OPTIONS);
}
let uploadImage = async(uri) => {
setMessage("上傳中");
const filename = uri.split('/').pop();
const response = await fetch(uri);
const blob = await response.blob();
// Create a reference
const ref = firebase.storage().ref().child(filename);
// Upload file
const snapshot = await ref.put(blob);
// getDownloadURL
const url = await snapshot.ref.getDownloadURL();
console.log("url:"+url);
setMessage("檔案已上傳");
}
let openImagePickerAsync = async () => {
let permissionResult = await ImagePicker.requestCameraRollPermissionsAsync();
if (permissionResult.granted === false) {
setMessage("未給予存取照片的權限");
return;
}
let pickerResult = await ImagePicker.launchImageLibraryAsync();
//console.log(pickerResult);
if (!pickerResult.cancelled) {
//if not cancelled
setSelectedImage({ localUri: pickerResult.uri });
uploadImage(pickerResult.uri);
}
}
return (
<View style={styles.container}>
<Image source={{ uri: selectedImage.localUri }} style={styles.logo} />
<Button onPress={openImagePickerAsync} title='選擇檔案'/>
<Text>{message}</Text>
</View>
);
}
作業
將url儲存在airtable或firestore。接下來就可以針對產品(或其他)的照片進行新增、刪除、修改。