uni-app 媒体功能
核心知识点
图片选择与上传
uni-app 提供了多种 API 用于图片的选择、拍摄和上传。
1. 选择图片
// 从相册选择图片
uni.chooseImage({
count: 9, // 最多选择9张
sizeType: ['original', 'compressed'], // 原图或压缩图
sourceType: ['album', 'camera'], // 相册或相机
success: (res) => {
console.log('选择成功:', res.tempFilePaths);
// 上传图片
uploadImages(res.tempFilePaths);
},
fail: (err) => {
console.log('选择失败:', err);
}
});
// 上传图片
function uploadImages(tempFilePaths) {
tempFilePaths.forEach((filePath, index) => {
uni.uploadFile({
url: 'https://api.example.com/upload',
filePath: filePath,
name: 'file',
formData: {
'index': index
},
success: (res) => {
console.log('上传成功:', res.data);
},
fail: (err) => {
console.log('上传失败:', err);
}
});
});
}2. 拍摄图片
// 调用相机拍摄
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['camera'], // 仅相机
success: (res) => {
console.log('拍摄成功:', res.tempFilePaths[0]);
},
fail: (err) => {
console.log('拍摄失败:', err);
}
});3. 预览图片
// 预览图片
uni.previewImage({
current: 'https://example.com/image.jpg', // 当前显示的图片链接
urls: ['https://example.com/image1.jpg', 'https://example.com/image2.jpg'], // 需要预览的图片链接列表
success: (res) => {
console.log('预览成功');
},
fail: (err) => {
console.log('预览失败:', err);
}
});音频视频处理
uni-app 提供了音频和视频的录制、播放和处理功能。
1. 音频录制与播放
// 开始录音
uni.startRecord({
success: () => {
console.log('开始录音');
},
fail: (err) => {
console.log('开始录音失败:', err);
}
});
// 停止录音
uni.stopRecord({
success: (res) => {
console.log('录音成功:', res.tempFilePath);
// 播放录音
playAudio(res.tempFilePath);
},
fail: (err) => {
console.log('录音失败:', err);
}
});
// 播放音频
function playAudio(filePath) {
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.src = filePath;
innerAudioContext.play();
innerAudioContext.onPlay(() => {
console.log('开始播放');
});
innerAudioContext.onEnded(() => {
console.log('播放结束');
});
innerAudioContext.onError((err) => {
console.log('播放失败:', err);
});
}2. 视频录制与播放
// 拍摄视频
uni.chooseVideo({
sourceType: ['camera'],
maxDuration: 60, // 最长60秒
camera: 'back', // 后置摄像头
success: (res) => {
console.log('拍摄成功:', res.tempFilePath);
// 上传视频
uploadVideo(res.tempFilePath);
},
fail: (err) => {
console.log('拍摄失败:', err);
}
});
// 上传视频
function uploadVideo(tempFilePath) {
uni.uploadFile({
url: 'https://api.example.com/upload',
filePath: tempFilePath,
name: 'file',
formData: {
'type': 'video'
},
success: (res) => {
console.log('上传成功:', res.data);
},
fail: (err) => {
console.log('上传失败:', err);
}
});
}
// 播放视频
uni.createVideoContext('myVideo').play();相机操作
uni-app 提供了相机相关的 API,用于控制相机的各项功能。
1. 相机上下文
// 创建相机上下文
const cameraContext = uni.createCameraContext();
// 拍摄照片
cameraContext.takePhoto({
quality: 'high',
success: (res) => {
console.log('拍摄成功:', res.tempImagePath);
},
fail: (err) => {
console.log('拍摄失败:', err);
}
});
// 开始录像
cameraContext.startRecord({
success: () => {
console.log('开始录像');
},
fail: (err) => {
console.log('开始录像失败:', err);
}
});
// 停止录像
cameraContext.stopRecord({
success: (res) => {
console.log('录像成功:', res.tempVideoPath);
},
fail: (err) => {
console.log('录像失败:', err);
}
});2. 扫码功能
// 扫码
uni.scanCode({
onlyFromCamera: false, // 是否仅从相机扫码
scanType: ['barCode', 'qrCode'], // 扫码类型
success: (res) => {
console.log('扫码成功:', res.result);
},
fail: (err) => {
console.log('扫码失败:', err);
}
});实用案例
实现图片上传功能
1. 单图上传
<template>
<view class="upload-container">
<view class="upload-area" @click="chooseImage">
<image v-if="imageUrl" :src="imageUrl" mode="aspectFill"></image>
<view v-else class="upload-placeholder">
<text>+ 选择图片</text>
</view>
</view>
<button v-if="imageUrl" class="upload-button" @click="uploadImage">上传图片</button>
</view>
</template>
<script>
export default {
data() {
return {
imageUrl: '',
tempFilePath: ''
};
},
methods: {
chooseImage() {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
this.tempFilePath = res.tempFilePaths[0];
this.imageUrl = res.tempFilePaths[0];
},
fail: (err) => {
console.log('选择失败:', err);
uni.showToast({
title: '选择失败',
icon: 'none'
});
}
});
},
uploadImage() {
if (!this.tempFilePath) {
uni.showToast({
title: '请先选择图片',
icon: 'none'
});
return;
}
uni.showLoading({
title: '上传中...'
});
uni.uploadFile({
url: 'https://api.example.com/upload',
filePath: this.tempFilePath,
name: 'file',
formData: {
'type': 'avatar'
},
success: (res) => {
console.log('上传成功:', res.data);
const data = JSON.parse(res.data);
if (data.code === 0) {
uni.showToast({
title: '上传成功',
icon: 'success'
});
// 保存服务器返回的图片地址
this.imageUrl = data.data.url;
} else {
uni.showToast({
title: '上传失败',
icon: 'none'
});
}
},
fail: (err) => {
console.log('上传失败:', err);
uni.showToast({
title: '上传失败',
icon: 'none'
});
},
complete: () => {
uni.hideLoading();
}
});
}
}
};
</script>
<style scoped>
.upload-container {
padding: 20rpx;
}
.upload-area {
width: 300rpx;
height: 300rpx;
border: 1rpx dashed #ddd;
border-radius: 8rpx;
overflow: hidden;
margin-bottom: 20rpx;
}
.upload-area image {
width: 100%;
height: 100%;
}
.upload-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
}
.upload-placeholder text {
font-size: 28rpx;
color: #999;
}
.upload-button {
width: 300rpx;
height: 80rpx;
background-color: #007aff;
color: #fff;
border-radius: 8rpx;
font-size: 28rpx;
}
</style>2. 多图上传
<template>
<view class="multi-upload-container">
<view class="upload-list">
<view v-for="(item, index) in images" :key="index" class="upload-item">
<image :src="item.url" mode="aspectFill"></image>
<view class="delete-btn" @click="deleteImage(index)">
<text>×</text>
</view>
</view>
<view v-if="images.length < 9" class="upload-item upload-add" @click="chooseImages">
<text>+</text>
</view>
</view>
<button v-if="images.length > 0" class="upload-button" @click="uploadImages">上传图片</button>
</view>
</template>
<script>
export default {
data() {
return {
images: []
};
},
methods: {
chooseImages() {
uni.chooseImage({
count: 9 - this.images.length, // 最多选择9张
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
// 添加选择的图片
const newImages = res.tempFilePaths.map(path => ({
url: path,
uploaded: false
}));
this.images = [...this.images, ...newImages];
},
fail: (err) => {
console.log('选择失败:', err);
uni.showToast({
title: '选择失败',
icon: 'none'
});
}
});
},
deleteImage(index) {
this.images.splice(index, 1);
},
uploadImages() {
if (this.images.length === 0) {
uni.showToast({
title: '请先选择图片',
icon: 'none'
});
return;
}
uni.showLoading({
title: '上传中...'
});
let uploadedCount = 0;
const totalCount = this.images.length;
this.images.forEach((item, index) => {
if (!item.uploaded) {
uni.uploadFile({
url: 'https://api.example.com/upload',
filePath: item.url,
name: 'file',
formData: {
'index': index
},
success: (res) => {
console.log('上传成功:', res.data);
const data = JSON.parse(res.data);
if (data.code === 0) {
// 更新为服务器返回的地址
this.images[index].url = data.data.url;
this.images[index].uploaded = true;
uploadedCount++;
if (uploadedCount === totalCount) {
uni.hideLoading();
uni.showToast({
title: '全部上传成功',
icon: 'success'
});
}
} else {
uni.hideLoading();
uni.showToast({
title: '上传失败',
icon: 'none'
});
}
},
fail: (err) => {
console.log('上传失败:', err);
uni.hideLoading();
uni.showToast({
title: '上传失败',
icon: 'none'
});
}
});
} else {
uploadedCount++;
}
});
}
}
};
</script>
<style scoped>
.multi-upload-container {
padding: 20rpx;
}
.upload-list {
display: flex;
flex-wrap: wrap;
margin-bottom: 20rpx;
}
.upload-item {
width: 220rpx;
height: 220rpx;
margin: 10rpx;
position: relative;
border-radius: 8rpx;
overflow: hidden;
}
.upload-item image {
width: 100%;
height: 100%;
}
.upload-add {
border: 1rpx dashed #ddd;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
}
.upload-add text {
font-size: 48rpx;
color: #999;
}
.delete-btn {
position: absolute;
top: 0;
right: 0;
width: 40rpx;
height: 40rpx;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
border-radius: 0 8rpx 0 20rpx;
}
.upload-button {
width: 100%;
height: 80rpx;
background-color: #007aff;
color: #fff;
border-radius: 8rpx;
font-size: 28rpx;
margin-top: 20rpx;
}
</style>实现音频录制与播放
<template>
<view class="audio-container">
<view class="audio-control">
<button v-if="!isRecording" class="record-button" @click="startRecord">开始录音</button>
<button v-else class="stop-button" @click="stopRecord">停止录音</button>
</view>
<view v-if="audioUrl" class="audio-playback">
<text>录音文件: {{ audioUrl }}</text>
<button class="play-button" @click="playAudio">播放录音</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
isRecording: false,
audioUrl: '',
innerAudioContext: null
};
},
onLoad() {
// 创建音频上下文
this.innerAudioContext = uni.createInnerAudioContext();
this.innerAudioContext.onPlay(() => {
console.log('开始播放');
});
this.innerAudioContext.onEnded(() => {
console.log('播放结束');
});
this.innerAudioContext.onError((err) => {
console.log('播放失败:', err);
uni.showToast({
title: '播放失败',
icon: 'none'
});
});
},
methods: {
startRecord() {
uni.startRecord({
success: () => {
this.isRecording = true;
uni.showToast({
title: '开始录音',
icon: 'none'
});
},
fail: (err) => {
console.log('开始录音失败:', err);
uni.showToast({
title: '开始录音失败',
icon: 'none'
});
}
});
},
stopRecord() {
uni.stopRecord({
success: (res) => {
this.isRecording = false;
this.audioUrl = res.tempFilePath;
uni.showToast({
title: '录音成功',
icon: 'success'
});
},
fail: (err) => {
console.log('录音失败:', err);
this.isRecording = false;
uni.showToast({
title: '录音失败',
icon: 'none'
});
}
});
},
playAudio() {
if (!this.audioUrl) return;
this.innerAudioContext.src = this.audioUrl;
this.innerAudioContext.play();
}
},
onUnload() {
// 销毁音频上下文
if (this.innerAudioContext) {
this.innerAudioContext.destroy();
}
}
};
</script>
<style scoped>
.audio-container {
padding: 20rpx;
}
.audio-control {
margin-bottom: 30rpx;
}
.record-button {
width: 200rpx;
height: 80rpx;
background-color: #ff3b30;
color: #fff;
border-radius: 8rpx;
font-size: 28rpx;
}
.stop-button {
width: 200rpx;
height: 80rpx;
background-color: #34c759;
color: #fff;
border-radius: 8rpx;
font-size: 28rpx;
}
.audio-playback {
margin-top: 30rpx;
padding: 20rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
}
.audio-playback text {
display: block;
margin-bottom: 20rpx;
font-size: 28rpx;
color: #333;
}
.play-button {
width: 200rpx;
height: 80rpx;
background-color: #007aff;
color: #fff;
border-radius: 8rpx;
font-size: 28rpx;
}
</style>实现相机扫码功能
<template>
<view class="scan-container">
<button class="scan-button" @click="scanCode">扫码</button>
<view v-if="scanResult" class="result-container">
<text class="result-label">扫码结果:</text>
<text class="result-text">{{ scanResult }}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
scanResult: ''
};
},
methods: {
scanCode() {
uni.scanCode({
onlyFromCamera: false,
scanType: ['barCode', 'qrCode'],
success: (res) => {
console.log('扫码成功:', res.result);
this.scanResult = res.result;
uni.showToast({
title: '扫码成功',
icon: 'success'
});
},
fail: (err) => {
console.log('扫码失败:', err);
uni.showToast({
title: '扫码失败',
icon: 'none'
});
}
});
}
}
};
</script>
<style scoped>
.scan-container {
padding: 20rpx;
}
.scan-button {
width: 200rpx;
height: 80rpx;
background-color: #007aff;
color: #fff;
border-radius: 8rpx;
font-size: 28rpx;
margin-bottom: 30rpx;
}
.result-container {
padding: 20rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
}
.result-label {
display: block;
margin-bottom: 10rpx;
font-size: 28rpx;
font-weight: bold;
color: #333;
}
.result-text {
font-size: 28rpx;
color: #666;
word-break: break-all;
}
</style>学习目标
通过本集的学习,你应该能够:
- 掌握 uni-app 图片选择与上传的 API 使用方法
- 学会音频和视频的录制、播放和处理
- 熟悉相机操作和扫码功能的实现
- 能够通过实用案例实现图片上传、音频录制与播放、相机扫码等功能
- 了解媒体功能的最佳实践,提高应用的用户体验
小结
媒体功能是 uni-app 开发中的重要部分,合理的媒体功能实现可以提高应用的用户体验和交互性。通过本集的学习,你已经掌握了图片选择与上传、音频视频处理、相机操作等核心知识点,并通过实际案例了解了如何实现图片上传、音频录制与播放、相机扫码等功能。在后续的开发中,你可以根据实际需求,灵活运用这些知识,构建更加功能强大的应用。