Camera Roll API + Upload From Camera Roll Module

Camera Roll API + Upload From Camera Roll Module

我們將介紹使用內置的React Native Camera Roll API顯示相機中的圖片。一旦用戶選擇了一張圖片,我們將使用Upload From Camera Roll Module獲得圖片的base64數據。如果你只是想讓用戶看到和選擇圖片,你可以使用這種方法,但如果是想拍照,你需要使用camera模塊. 如果不想讓用戶定義和選擇圖片的界面,你可以使用 Image Picker Module.

導入CameraRoll API

const React = require('react-native');

const {
    CameraRoll,
} = React;

使用CameraRoll中的getPhotos方法,獲得圖片列表.

CameraRoll.getPhotos接受 3 個參數:第一個參數是一個對像,它定義了從相機膠捲中返回什麼樣的圖片。第二個是一個回調函數,這個函數,接受請求到的圖片,第三個參數也是一個回調函數,它用來處理錯誤。首先,讓我們看看第一個參數,它是如何定義要返回的圖片。以下是這個對數的屬性。

{
    first: ..., // (必須的) 在照片中,按反相排序後,在獲取的照片的數量
    after: ..., // 上一次調用getPhotos返回的指針。cursor
    groupTypes: ..., // 指定分組類型,過濾結果
                     // One of ['Album', 'All', 'Event', 'Faces', 'Library', 'PhotoStream', 'SavedPhotos'(default)]
    groupName: ..., // Specifies filter on group names, like 'Recent Photos' or custom album titles
    assetType: ... // Specifies filter on assetType
                   // One of ['All', 'Videos', 'Photos'(default)]
}

 對於我們的例子來說,我們想要從camera roll 中獲取前25張圖片,getPhotos的第一個參數爲:

const fetchParams = {
    first: 25,
}

下一步,我們定義一個回調函數來處理接收到的圖片。要傳遞給這個回調函數的數據,是從CameraRool.getPhotos中獲得,這個數據是一個對像,它包含了一個數組,這個數組包含了所有的圖片節點。另一個是一個對像,它包含了分頁信息。這個對像如下:

{
    edges: [
        node: {
            type: ...,
            group_name: ...,
            image: {
                uri: ...,
                height: ...,
                width: ...,
                isStored: ...,
            },
            timestamp: ...,
            location {
                ...
            },
        },
        node: { ... },
        node: { ... },
        ...
    ],
    page_info:  {
        has_next_page: ...,
        start_cursor: ...,
        end_cursor: ...,
    }
}

由於節點中的image對像包含了我們將要在App中顯示圖片的時要使用到的數據,所以我們需要創建一個函數,從edges數組中提取image對像。並且應該將它們保存到一個state變量中.

storeImages(data) {
    const assets = data.edges;
    const images = assets.map( asset => asset.node.image );
    this.setState({
        images: images,
    });
 },

由於我們使用了images變量,所以需要先定義這個變量

getInitialState() {
    return {
        images: [],
    };
},

getPhotos的第三個參數是用來處理錯誤的回調函數。對於這個例子,我們只是將錯誤發送到控制檯.

logImageError(err) {
    console.log(err);
},


到目前爲此,我們已經爲CameraRoll.getPhotos定義了三個參數。我們將在ComponentDidMount()中調用這個getPhotos,  它會檢索一次圖片信息。

componentDidMount() {
    const fetchParams = {
        first: 25,
    };
    CameraRoll.getPhotos(fetchParams, this.storeImages, this.logImageError);
},

讀取圖片

我們可以使用Image組件,flexbox樣式,以及其它的map function來顯示檢索到的圖片。

讓我們先導入StyleSheet和Image組件

const {
    CameraRoll,
    StyleSheet,
    Image,
} = React;

然後在render函數中顯示圖片

render() {
    return (
        <ScrollView style={styles.container}>
            <View style={styles.imageGrid}>
            { this.state.images.map(image => <Image style={styles.image} source={{ uri: image.uri }} />) }
            </View>
        </ScrollView>
    );
  }

然後添加樣式

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#F5FCFF',
    },
    imageGrid: {
        flex: 1,
        flexDirection: 'row',
        flexWrap: 'wrap',
        justifyContent: 'center'
    },
    image: {
        width: 100,
        height: 100,
        margin: 10,
    },
});


到目前爲此,你的組件看起來如下

const React = require('react-native');

const {
  StyleSheet,
  Text,
  View,
  ScrollView,
  Image,
  CameraRoll,
} = React;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5FCFF',
  },
  imageGrid: {
    flex: 1,
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'center'
  },
  image: {
    width: 100,
    height: 100,
    margin: 10,
  },
});

const reactImageProject = React.createClass({
  getInitialState() {
    return {
      images: [],
    };
  },

  componentDidMount() {
    const fetchParams = {
      first: 25,
    };
    CameraRoll.getPhotos(fetchParams, this.storeImages, this.logImageError);
  },

  storeImages(data) {
    const assets = data.edges;
    const images = assets.map((asset) => asset.node.image);
    this.setState({
      images: images,
    });
  },

  logImageError(err) {
    console.log(err);
  },

  render() {
    return (
      <ScrollView style={styles.container}>
        <View style={styles.imageGrid}>
          { this.state.images.map((image) => <Image style={styles.image} source={{ uri: image.uri }} />) }
        </View>
      </ScrollView>
    );
  }
});


當你運行這個項目,你會看到一個最新相機中最新的25張圖片




選擇圖片

爲了讓用戶可以選擇其中一張圖片,我們冉要讓上面的圖片可以被點擊。我們需要使用TouchableHighlight組件,當點擊圖片是,將圖片的uri保存到另一個state變量中.

我們首先測試選擇到圖片的uri

selectImage(uri) {
  this.setState({
    selected: uri,
  });
  console.log('Selected image: ', uri);
},

同樣,需要在getInitialSate()中定義

getInitialState() {
  return {
    images: [],
    selected: '',
  };
},

以下是新的render函數

const {
  StyleSheet,
  Text,
  View,
  ScrollView,
  Image,
  CameraRoll,
  TouchableHighlight,
} = React;

render() {
  return (
    <ScrollView style={styles.container}>
      <View style={styles.imageGrid}>
        { this.state.images.map((image) => {
            return (
              <TouchableHighlight onPress={this.selectImage.bind(null, image.uri)}>
                <Image style={styles.image} source={{ uri: image.uri }} />
              </TouchableHighlight>
            );
          })
        }
      </View>
    </ScrollView>
  );
}

當你運行這個項目時,點擊圖片會輸出圖片的uri到console.

獲得圖片的base64數據

已經存在一個模塊, 它使用React native image 組件獲得這個圖片的base64版本。但它不能通過NPM安裝。所以我們僅要在創建自定義模塊是,將它添加進來。如upload-from-camera-roll中有介紹, 如何在你的項目中包含upload-form-camera-roll模塊。

現在,我們可以使用這個模塊從image的uri中,來獲得base64編碼的圖片數據。 我們可以改變selectImage函數,把原來輸出uri的功能,改成保存和輸出圖片的base64編碼.

selectImage(uri) {
    NativeModules.ReadImageData.readImage(uri, (image) => {
        this.setState({
            selected: image,
        });
        console.log(image);
    });
},


完整代碼如下

const React = require('react-native');

const {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    ScrollView,
    Image,
    CameraRoll,
    TouchableHighlight,
    NativeModules,
} = React;

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#F5FCFF',
    },
    imageGrid: {
        flex: 1,
        flexDirection: 'row',
        flexWrap: 'wrap',
        justifyContent: 'center'
    },
    image: {
        width: 100,
        height: 100,
        margin: 10,
    }
});

const reactImageProject = React.createClass({
    getInitialState() {
        return {
            images: [],
            selected: '',
        };
    },

    componentDidMount() {
        const fetchParams = {
            first: 25,
        };
        CameraRoll.getPhotos(fetchParams, this.storeImages, this.logImageError);
    },

    storeImages(data) {
        const assets = data.edges;
        const images = assets.map((asset) => asset.node.image);
        this.setState({
            images: images,
        });
    },

    logImageError(err) {
        console.log(err);
    },

    selectImage(uri) {
        NativeModules.ReadImageData.readImage(uri, (image) => {
            this.setState({
                selected: image,
            });
            console.log(image);
        });
    },

    render() {
        return (
            <ScrollView style={styles.container}>
                <View style={styles.imageGrid}>
                { this.state.images.map((image) => {
                    return (
                        <TouchableHighlight onPress={this.selectImage.bind(null, image.uri)}>
                        <Image style={styles.image} source={{ uri: image.uri }} />
                        </TouchableHighlight>
                    );
                    })
                }
                </View>
            </ScrollView>
        );
    }
});

AppRegistry.registerComponent('reactImageProject', () => reactImageProject);


關於NativeModules模塊

我們可以在獲取到的圖片數據中,添加一個base64屬性到image對像. 如下

在Xcode中打開Libraries > RCTImage > RCTCameraRollManager.m中打開


替換這個文件爲

https://github.com/scottdixon/react-native-upload-from-camera-roll/blob/master/RCTCameraRollManager.m


現在你獲得的圖片對像就會包含base64屬性。你僅需要調整相關的React Native文件,讓它知道新的屬性

打開/node_modules/react-native/Libraries/CameraRoll/CameraRoll.js,將下面的行,添加到76行

base64: ReactPropTypes.string,

如果你打算使用這個方法上傳全分辨率的圖片,應用程序會運行的超慢,因爲它需要將圖片轉換爲大型的base64字符串。

爲了更好的優化,我們創建一個自定義的本地模塊,它充許我們的Javascript 與Object-c通話,通過這種方式,我們給 Object-C傳遞一個圖片資源的URI給它,Object-c在返回這個圖片的base64數據。現在iOS僅在需要的時候讀取一個圖片。相對於上面的方法,要讀取所有的圖片。這是一個非常大的優化 。

創建一個自定義模塊,非常簡單。

在Xcode中 ,打開Project > Libraries > React > Base. 在Base上右鍵,選擇New File... ,在選擇Object-C文件。命名爲RCTCustom.m, 並且添加以下的內容。這就創建了一個ReadImageData的模塊了.


#import "RCTBridgeModule.h"
#import <AssetsLibrary/AssetsLibrary.h>
#import <UIKit/UIKit.h>
@interface ReadImageData : NSObject <RCTBridgeModule>
@end

@implementation ReadImageData

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(readImage:(NSString *)input callback:(RCTResponseSenderBlock)callback)
{

  // Create NSURL from uri
  NSURL *url = [[NSURL alloc] initWithString:input];
  
  // Create an ALAssetsLibrary instance. This provides access to the
  // videos and photos that are under the control of the Photos application.
  ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
  
  // Using the ALAssetsLibrary instance and our NSURL object open the image.
  [library assetForURL:url resultBlock:^(ALAsset *asset) {
    
    // Create an ALAssetRepresentation object using our asset
    // and turn it into a bitmap using the CGImageRef opaque type.
    CGImageRef imageRef = [asset thumbnail];
    // Create UIImageJPEGRepresentation from CGImageRef
    NSData *imageData = UIImageJPEGRepresentation([UIImage imageWithCGImage:imageRef], 0.1);
    
    // Convert to base64 encoded string
    NSString *base64Encoded = [imageData base64EncodedStringWithOptions:0];
    
    callback(@[base64Encoded]);
    
  } failureBlock:^(NSError *error) {
    NSLog(@"that didn't work %@", error);
  }];
  

  
}
@end

現在你的Xcode project 已經有了一個新的自定義模塊,我們可以在Javascript中導入這個組件.

var {View, Text, Image, NativeModules} = React;

然後我們可以能過NativeModules訪問我們 new module

NativeModules.ReadImageData.readImage(ourImage.node.image.uri, (image) => {
  console.log(image)
})

然後,能過fetch方法,上傳圖片資源

fetch('http://your.server/app.php', {
method:'POST',
headers: {
  'Accept': 'application/json',
  'Content-Type': 'application/json',
  body: JSON.stringify({imageData:image})
}
})






發佈了44 篇原創文章 · 獲贊 11 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章