照片获取与保存
官方提供了CameraRoll
模块访问本地相册的功能,它包含两个方法:
方法 | 描述 |
---|---|
CameraRoll.getPhotos(params); | 表示获取相册数据,参数格式如下: { first:number类型,要获取的照片数 assetType:All | Videos | Photos,默认为Photos after:用于记录上一次获取到哪一张图片 ... // 其它不常用可看官方文档解释 } |
CameraRoll.saveToCameraRoll(tag, [type]); | 在 Android 上,tag是一个本地 URI,例如"file:///sdcard/img.png"。 在 iOS, tag可以是任意图片 URI。 如果后缀名为.mov或.mp4,则保存为视频,其它后缀均保存为图片。 如果你需要自定义保存类型,可以在第二个参数type中定义,类型只能是 'photo' 或者 'video' |
现在虚拟机中有三张照片,每次获取并更新一张,完整demo如下:关于请求权限问题,可以浏览上一篇博客《react-native系列(19)API篇:请求系统权限(相册等)的方法》
import React from 'react';
import { View, Text, Button, Image, Platform, CameraRoll, PermissionsAndroid, Alert } from 'react-native';
class CameraRollAPI extends React.Component {
state = {
images: [], // 存放照片数据
cursorEnd: '', // 上一次相册中获取照片的结束标记
hasNextPage: true // 是否还有下一张照片
}
UNSAFE_componentWillMount() {
this.requestReadPermission();
}
getPhotos(){
if(this.state.hasNextPage){
let queryNativePictuire = {
first: 1,
assetType: 'Photos' // All全部|Videos录像|Photos照片(默认)
};
if (this.state.cursorEnd) {
queryNativePictuire.after = this.state.cursorEnd;
}
CameraRoll.getPhotos(queryNativePictuire).then((result) => {
let pageInfo = result.page_info;
this.setState({ images: result.edges, cursorEnd: pageInfo.end_cursor, hasNextPage: pageInfo.has_next_page });
}).catch((error) => {
console.log(error);
});
}else{
Alert.alert('提示','已经没有下一张照片');
}
}
async requestReadPermission() {
try {
const os = Platform.OS; // android or ios
if(os === 'android'){
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
//第一次请求【拒绝】后提示用户你为什么要这个权限
'title': '我要读写权限',
'message': '没权限我不能工作,同意就好了'
}
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
// 已获取了读写权限
this.getPhotos();
} else {
// 获取读写权限失败
}
}else{
this.getPhotos();
}
} catch (err) {
console.log(err.toString());
}
}
_getNextPhoto = () => {
this.getPhotos();
}
_saveImg = () => {
const img = 'http://www.helloui.net/assets/watch.jpg'; // 网络图片在android中会报错
var promise = CameraRoll.saveToCameraRoll(img); // 在 Android 上,img是一个本地 URI,例如"file:///sdcard/img.png"。在 iOS, img可以是任意图片 URI
promise.then(function(result) {
alert('保存成功!地址如下:\n' + result);
}).catch(function(error) {
alert('保存失败!\n' + error);
});
}
_renderImg = () => {
const side = 200 ;
if(this.state.images.length > 0){
const image = this.state.images[0].node.image;
return(
<View>
<Image
source={{ uri: image.uri }}
style={{width:side, height:side}}
/>
</View>
);
}else{
return null;
}
}
render(){
return(
<View>
<Text>照片:</Text>
{this._renderImg()}
<Button
title='更换一张照片'
onPress={this._getNextPhoto}
/>
<Button
title='保存一张网络图片'
onPress={this._saveImg}
/>
</View>
);
}
}
export default CameraRollAPI;
效果:
照片选择
在app需求中,常常从相册中选取一张照片或视频然后上传。这种情况最常用的是react-native-image-picker插件,它即可以适用于照片也可以适用于视频,通过该插件可以很方便的获取想要的照片或视频数据。
安装:
npm install react-native-image-picker@latest --save
react-native link
安装成功后,如果使用VSCode编辑器,可能会存在一个bug:打开android/settings.gradle文件,正常情况下会看到如下脚本:
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
include ':react-native-image-picker'
project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
这表示你的安装时正确的。如果后面的路径 ' / ' 变为反斜杠 ' \ ' 将无法使用,改正过来即可。
完整demo代码:
import React from 'react';
import { View, Button, Image } from 'react-native';
import ImagePicker from 'react-native-image-picker';
class ImagePickerAPI extends React.Component {
state = {
avatarSource: {
uri: ''
}
}
onPressImg = () => {
const options = {
title: '选择图片',
cancelButtonTitle: '取消',
takePhotoButtonTitle: '拍照',
chooseFromLibraryButtonTitle: '相册',
customButtons: [
{name: '唯一名称', title: '显示的自定义按钮内容'},
],
cameraType: 'back', // 类型 'front' or 'back' ,前置还是后置摄像头
mediaType: 'photo', // 图片或视频 'photo', 'video', or 'mixed' on iOS, 'photo' or 'video' on Android
videoQuality: 'high', // 视频质量 'low', 'medium', or 'high' on iOS, 'low' or 'high' on Android
durationLimit: 10, // 最大录制时间
maxWidth: 300, // 返回的图片数据的最大宽 photos only 只有图片有这个属性
maxHeight: 300, // 返回的图片数据的最大高 photos only
quality: 0.8, // 图片质量 0 to 1 photos only
angle: 90,
allowsEditing: false, // 是否可以编辑
noData: false, // 如果为true,则禁用data生成的base64 字段(极大地提高大图片的性能)
storageOptions: {
skipBackup: true // 如果true,该照片将不会备份到iCloud
}
};
ImagePicker.showImagePicker(options, (response) => {
if (response.didCancel) {
console.log('取消');
}
else if (response.error) {
console.log('发生错误: ', response.error);
}
else if (response.customButton) {
console.log('自定义按钮被点击,它的名称是:', response.customButton);
}
else {
console.log('Response = ', response); // 选择或拍摄的照片数据对象
let source = { uri: response.uri };
// You can also display the image using data:
// let source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
avatarSource: source
});
}
});
}
onPressVideo = () => {
const options = {
title: '选择视频',
cancelButtonTitle: '取消',
takePhotoButtonTitle: '录制视频',
chooseFromLibraryButtonTitle: '选择视频',
mediaType: 'video',
videoQuality: 'medium' // 视频质量 'low', 'medium', or 'high' on iOS, 'low' or 'high' on Android
};
ImagePicker.showImagePicker(options, (response) => {
if (response.didCancel) {
console.log('取消');
}
else if (response.error) {
console.log('发生错误: ', response.error);
}
else if (response.customButton) {
console.log('自定义按钮被点击,它的名称是:', response.customButton);
}
else {
console.log('Response = ', response); // 选择或录取的视频数据对象
this.setState({
videoSource: response.uri
});
}
});
}
_renderImg = () => {
const side = 200 ;
if(this.state.avatarSource.uri){
return(
<View>
<Image
source={{ uri: this.state.avatarSource.uri }}
style={{width:side, height:side}}
/>
</View>
);
}else{
return null;
}
}
render(){
return(
<View>
<Button
title='照片'
onPress={this.onPressImg}
/>
<Button
title='视频'
onPress={this.onPressVideo}
/>
{this._renderImg()}
</View>
);
}
}
export default ImagePickerAPI;
效果:
照片上传
使用FormData数据格式封装后上传,核心代码如下:
const file = {
uri: imageURI, // 本地图片路径
name: `${uuid()}.jpg`, // 图片名称
type: 'image/jpg' // 图片格式
};
const body = new FormData();
body.append('file',file);
fetch(FILE_UPLOAD_URL, {
method: 'POST',
body,
headers: {
'Accept': 'application/json', // 返回json数据格式
'Content-Type':'multipart/form-data'
}
})
.then(checkStatus)
.then(response => response.json())
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});