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张图片



Camera Roll API + Upload From Camera Roll Module_第1张图片

选择图片

为了让用户可以选择其中一张图片,我们冉要让上面的图片可以被点击。我们需要使用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中打开
Camera Roll API + Upload From Camera Roll Module_第2张图片

替换这个文件为

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






你可能感兴趣的:(react,Camera,native,Roll)