本文主要介绍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图了