ReactNative 图片上传保存 CameraRoll的使用

本文主要介绍CaremaRoll的使用(保存图片,获取手机图片) 图片上传(常用于上传头像功能)

CaremaRoll介绍

结合官方文档做说明:

CameraRoll模块提供了访问本地相册的功能。在iOS上使用这个模块之前,你需要先链接RCTCameraRoll库,具体做法请参考链接原生库文档。

译注:本模块只提供了基本的访问图片的功能,并没有提供相册界面。对于多数开发者来说,可能第三方的react-native-image-crop-picker的功能更为完整易用(可多选、压缩、裁剪等)。

iOS 10的权限要求

从iOS10开始,访问相册需要用户授权。你需要在Info.plist中添加一条名为NSPhotoLibraryUsageDescription的键,然后在其值中填写向用户请求权限的具体描述。编辑完成后这个键在Xcode中实际会显示为Privacy - Photo Library Usage Description。

iOS 11的权限要求

从iOS11开始,如果您的项目有保存图片需求,需要额外申请用户授权。你需要在Info.plist中添加一条名为NSPhotoLibraryAddUsageDescription的键,然后在其值中填写向用户请求权限的具体描述。编辑完成后这个键在Xcode中实际会显示为Privacy - Photo Library Additions Usage Description。而名为NSPhotoLibraryUsageDescription的键此时仅控制相册的读取。具体说明请翻阅官方文档搜索相关键值。

我们可以看到,一共有两个权限需要声明, 一个是读取相册,一个是保存图片.
虽然有第三方,但我想自己用基础的API来实现这个功能 , 也能让以后对这方面的东西接触的时候有更好的理解.

然后看一下API:

static saveToCameraRoll(tag, type?)

(1)保存一个图片到相册。
(2)@param {string} tag

在安卓上,本参数是一个本地URI,例如”file:///sdcard/img.png”.
在iOS设备上可能是以下之一:

  • 本地URI
  • 资源库的标签
  • 非以上两种类型,表示图片数据将会存储在内存中(并且在本进程持续的时候一直会占用内存),
  • tag 可以是 url、assets-library、内存图片中的一种。

(3)参数 type 不是必须的,可选值是’photo’ 或 ‘video’。用来表示存的是图片还是视频。不指定的话程序也会根据后缀自行判断。(结尾为 .mov 或 .mp4 为视频,其它为图片)

返回一个Promise,操作成功时返回新的URI。

static getPhotos(params: object)

把图片或视频保存到相册中。
返回一个带有图片标识符对象的Promise。返回的对象的结构参见getPhotosReturnChecker。

params:对象 一些筛选的规则 有4个成员变量

1.first 数值 希望获取多少张图片的信息

2.groupTypes 字符串 默认为SavedPhotos [Album All Event Faces Library PhotoStream] 仅支持IOS平台 用来指定获取图片或视频的类型

3.assetType 字符串 默认为Photos 表示只获取图片 [All Videos]

4.after 字符串 用来记录上一次获取图片的结束标志 方便可以接着上次的位置继续获取 它的值不能由开发者随意赋予,而是应当在上一次获取图片后保存其值。通常,在Android平台,一开始就给这个值为null,但是在IOS平台,设置为null会抛一个无法捕捉的异常,导致红屏。

{
first: 5, 
groupTypes: 'All', 
assetType: 'Photos',
//after:null
}

返回一个Promise,操作成功时返回符合getPhotosReturnChecker结构的对象。
我再后台打印出来的数据

介绍一下使用方法

保存图片

    saveImg(img) {
        let promise = CameraRoll.saveToCameraRoll(img);
        promise.then(function (result) {
            alert('保存成功!地址如下:\n' + result);
        }).catch(function (error) {
            alert('保存失败!\n' + error);
        });
    }

也可以直接写一句代码:

 CameraRoll.saveToCameraRoll(img).then(result => {
            alert('保存成功!地址如下:\n' + result);
        }).catch(error => {
            alert('保存失败!\n' + error);
        })

这里的参数img 传来的是一个图片地址 :

let imgURL = "https://ss1.baidu.com/-4o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=8d3a9ea62c7f9e2f6f351b082f31e962/500fd9f9d72a6059099ccd5a2334349b023bbae5.jpg"

读取相册

这里我拆分成了两个方法, 一个方法是获取promise对象, 第二个方法是对这个promise进行分析,拿出我需要的数据.
先看第一个方法 , 前面先不用看, 中间是定义已下param参数, 最后是执行方法,拿到promise.
然后把这个promise给方法2进行分析,取数据.

    //获取图片列表
    getImages() {
        if (this.state.nomore) {
            alert('没有更多图片了');
            return
        }
        let param = {first: 5, groupTypes: 'All', assetType: 'Photos'};
        // 如果不是第一次取图片,则this.state.lastCursor不为空,下一次取图片时就从上次的结尾开始取
        if (this.state.lastCursor) {
            param.after = this.state.lastCursor;
        }

        CameraRoll.getPhotos(param).then(data => {
            console.log(data)
            this._appendAssets(data); // 取到图片数据后,交由appendAssets处理
        }, (e) => {
        }).done();
    }

这里,我先遍历了一下data.edges ,这是一个图片信息的数组, node.image.uri是图片的uri地址 , 上面图片的数据就是我这里得到的 . 遍历拿到这个uri就可以了.
data.page_info.has_next_page 这个是判断是否还有没加载的图片, 有就是true/没有未加载的就是false.
如果我们需要分页加载之类的功能 这个就派上用场了,也是我第一个方法中用来判断是否有更多图片的参数.
data.page_info.end_cursor 是这次获取到的所有图片里最后一个图片的uri , 我们下次再去读相册,就可以从这个开始加载了. 可以实现分页功能 .

    _appendAssets(data) {
        let newState = {};
        let edges = data.edges;
        let photos = [];
        for (let i in edges) {
            photos.push(edges[i].node.image.uri);
        }
        console.log(photos)
        if (!data.page_info.has_next_page) { //已经到相册的末尾了
            newState.noMore = true;
        }
        if (photos.length > 0) { //如果此次加载的图片数量大于0
            newState.lastCursor = data.page_info.end_cursor;
            newState.photos = this.state.photos.concat(photos);
            this.setState(newState)
        }

    }

获取到的相册图片用list来展示.
距离底部0.1的时候就去尝试获取相册图片, 每次获取5张, 直到全部加载完成. 这里的0.1是比例,不是px.

<FlatList
                    data={this.state.photos}
                    renderItem={({item}) =>
                        <TouchableOpacity onPress={() => this.setState({
                            img: item
                        })}>
                            <Image
                                source={{uri: item}}
                                style={{width: 250, height: 250, resizeMode: 'stretch'}}/>
                        </TouchableOpacity>}
                    onEndReachedThreshold={0.1}
                    onEndReached={() => this.getImages()}
                />

上传图片

首先要有后台 我这里没有 所以就没有实现这个功能, 但是代码是完成了的.
uri 就是图片的uri, 跟我们前面的一样. token 是用来鉴别用户的.
里面还有个参数url 是后台接收图片的地址
这个应该比较好理解了.

    upLoadImg(uri, token) {
        let imgFile = new FromData();
        imgFile.append('file', {uri, type: 'application/octet-stream', name: 'head.jpg'})
        fetch('url', {
            method: 'POST', headers: {
                'Content-Type': 'multipart/form-data;charset=utf-8',
                "x-access-token": token,
            },
            body: imgFile,
        }).then()
    }

最后放上源代码:

'use strict';
import React, {Component} from 'react';
import {
    StyleSheet,
    Text,
    View,
    TouchableOpacity,
    CameraRoll,
    Image,
    FlatList
} from 'react-native';

//网络图片地址
let imgURL = "https://ss1.baidu.com/-4o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=8d3a9ea62c7f9e2f6f351b082f31e962/500fd9f9d72a6059099ccd5a2334349b023bbae5.jpg"

export default class Appme extends Component {

    //构造函数
    constructor(props) {
        super(props);
        this.state = {
            photos: [],
            lastCursor: '',
            nomore: false
        };
    }

    componentDidMount() {
        this.getImages();
    }

    render() {

        let data = [{title: 'a'}, {title: 'a'}, {title: 'a'}, {title: 'a'},];
        return (
            <View style={styles.container}>
                <View style={styles.image}>
                    <Image style={styles.img}
                           source={{uri: this.state.img}}
                           resizeMode="contain"/>
                </View>
                <View>
                    <Text onPress={this.saveImg.bind(this, imgURL)} style={[styles.saveImg]}>
                        保存图片到相册
                    </Text>
                </View>
                <View>
                    <Text onPress={() => this.getImages()} style={[styles.saveImg]}>
                        获取图片列表
                    </Text>
                </View>

                <FlatList
                    data={this.state.photos}
                    renderItem={({item}) =>
                        <TouchableOpacity onPress={() => this.setState({
                            img: item
                        })}>
                            <Image
                                source={{uri: item}}
                                style={{width: 250, height: 250, resizeMode: 'stretch'}}/>
                        </TouchableOpacity>}
                    onEndReachedThreshold={0.1}
                    onEndReached={() => this.getImages()}
                />
            </View>
        )
    }


    //保存图片
    saveImg(img) {
        var promise = CameraRoll.saveToCameraRoll(img);
        promise.then(function (result) {
            alert('保存成功!地址如下:\n' + result);
        }).catch(function (error) {
            alert('保存失败!\n' + error);
        });

        CameraRoll.saveToCameraRoll(img).then(result => {
            alert('保存成功!地址如下:\n' + result);
        }).catch(error => {
            alert('保存失败!\n' + error);
        })
    }


    //获取图片列表
    getImages() {
        if (this.state.nomore) {
            alert('没有更多图片了');
            return
        }
        let param = {first: 5, groupTypes: 'All', assetType: 'Photos'};
        // 如果不是第一次取图片,则this.state.lastCursor不为空,下一次取图片时就从上次的结尾开始取
        if (this.state.lastCursor) {
            param.after = this.state.lastCursor;
        }

        CameraRoll.getPhotos(param).then(data => {
            console.log(data)
            this._appendAssets(data); // 取到图片数据后,交由appendAssets处理
        }, (e) => {
        }).done();
    }

    _appendAssets(data) {
        let newState = {};
        let edges = data.edges;
        let photos = [];
        for (let i in edges) {
            photos.push(edges[i].node.image.uri);
        }
        console.log(photos)
        if (!data.page_info.has_next_page) { //已经到相册的末尾了
            newState.noMore = true;
        }
        if (photos.length > 0) { //如果此次加载的图片数量大于0
            newState.lastCursor = data.page_info.end_cursor;
            newState.photos = this.state.photos.concat(photos);
            this.setState(newState)
        }

    }

    upLoadImg(uri, token) {
        let imgFile = new FromData();
        imgFile.append('file', {uri, type: 'application/octet-stream', name: 'head.jpg'})
        fetch('url', {
            method: 'POST', headers: {
                'Content-Type': 'multipart/form-data;charset=utf-8',
                "x-access-token": token,
            },
            body: imgFile,
        }).then()
    }
}


const styles = StyleSheet.create({
    container: {
        flex: 1,
        // justifyContent: 'center',
        backgroundColor: 'white',
        // alignItems: 'center'
    },
    welcome: {
        marginTop: 20,
        fontSize: 20,
    },
    image: {
        borderWidth: 1,
        width: 300,
        height: 100,
        borderRadius: 5,
        borderColor: '#ccc',
        marginTop: 50
    },
    img: {
        height: 98,
        width: 300,
        resizeMode: 'stretch'
    },
    saveImg: {
        height: 30,
        padding: 6,
        textAlign: 'center',
        backgroundColor: '#3BC1FF',
        color: '#FFF',
        marginTop: 10,
    }
});

本文仅提供思路 所以 有点丑…
就不放UI图了

项目gitub地址

猜你喜欢

转载自blog.csdn.net/u011272795/article/details/79075641