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;
}
}
}

這樣可以讓所有人可以讀取檔案,但是,要更動(如:上傳檔案)就必須先登入。

firebase cloud storage.ppt
  • 進入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。接下來就可以針對產品(或其他)的照片進行新增、刪除、修改。