uni-app 云存储使用

章节介绍

文件存储是应用开发中常见的需求,用于存储图片、视频、音频、文档等文件。uni-app 提供了基于 uniCloud 的云存储服务,开发者可以通过云存储实现文件的上传、下载、删除、管理等操作,无需关心存储服务的搭建、配置和维护。本章节将详细介绍如何使用 uni-app 云存储,包括云存储的概念、文件上传、下载、删除、管理等操作,帮助开发者高效地管理和使用云存储服务。

核心知识点讲解

1. 云存储概述

uniCloud 云存储是一种对象存储服务,用于存储和管理文件,具有以下特点:

  • 无需搭建和维护:开发者无需关心存储服务的搭建、配置、监控和维护
  • 高可靠性:数据多副本存储,确保数据安全可靠
  • 高并发:支持高并发文件上传和下载,应对大规模应用场景
  • 弹性扩展:根据存储需求自动调整存储容量
  • 易于使用:提供简洁的 API,与前端开发无缝集成
  • 支持多种文件类型:支持图片、视频、音频、文档等多种文件类型
  • CDN 加速:文件访问通过 CDN 加速,提高访问速度

2. 云存储的开通与配置

2.1 开通云存储服务

在使用云存储之前,需要开通 uniCloud 服务并创建服务空间:

  1. 注册并登录 DCloud 开发者账号

  2. 开通 uniCloud 服务

    • 在 DCloud 开发者中心中开通 uniCloud 服务
    • 创建服务空间(选择阿里云或腾讯云)
    • 记录服务空间 ID,用于后续配置

2.2 配置 HBuilderX

  • 下载并安装最新版本的 HBuilderX
  • 在 HBuilderX 中登录 DCloud 开发者账号
  • 关联服务空间:点击 "uniCloud" > "关联服务空间或项目",选择已创建的服务空间

3. 云存储的基本操作

3.1 文件上传

文件上传是云存储的核心功能之一,用于将本地文件上传到云端。uni-app 提供了多种上传方式:

客户端直接上传

async function uploadFile() {
  try {
    // 选择文件
    const [chooseResult] = await uni.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera']
    });
    
    // 上传文件到 uniCloud
    const uploadResult = await uniCloud.uploadFile({
      filePath: chooseResult.tempFilePaths[0],
      cloudPath: 'images/' + Date.now() + '.jpg'
    });
    
    if (uploadResult.success) {
      console.log('文件上传成功:', uploadResult.fileID);
      return uploadResult.fileID;
    } else {
      console.error('文件上传失败:', uploadResult.errMsg);
      throw new Error(uploadResult.errMsg);
    }
  } catch (error) {
    console.error('上传失败:', error);
    throw error;
  }
}

// 调用函数
uploadFile();

上传进度监听

async function uploadFileWithProgress() {
  try {
    // 选择文件
    const [chooseResult] = await uni.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera']
    });
    
    // 上传文件到 uniCloud
    const uploadResult = await uniCloud.uploadFile({
      filePath: chooseResult.tempFilePaths[0],
      cloudPath: 'images/' + Date.now() + '.jpg',
      onProgressUpdate: (progress) => {
        console.log('上传进度:', progress.progress);
        console.log('已上传字节数:', progress.totalBytesSent);
        console.log('总字节数:', progress.totalBytesExpectedToSend);
        // 可以在这里更新 UI 显示上传进度
      }
    });
    
    if (uploadResult.success) {
      console.log('文件上传成功:', uploadResult.fileID);
      return uploadResult.fileID;
    } else {
      console.error('文件上传失败:', uploadResult.errMsg);
      throw new Error(uploadResult.errMsg);
    }
  } catch (error) {
    console.error('上传失败:', error);
    throw error;
  }
}

// 调用函数
uploadFileWithProgress();

批量上传

async function uploadMultipleFiles() {
  try {
    // 选择多个文件
    const [chooseResult] = await uni.chooseImage({
      count: 3,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera']
    });
    
    // 批量上传文件
    const uploadPromises = chooseResult.tempFilePaths.map((filePath, index) => {
      return uniCloud.uploadFile({
        filePath,
        cloudPath: 'images/' + Date.now() + '_' + index + '.jpg'
      });
    });
    
    const uploadResults = await Promise.all(uploadPromises);
    const fileIDs = [];
    
    for (const result of uploadResults) {
      if (result.success) {
        fileIDs.push(result.fileID);
        console.log('文件上传成功:', result.fileID);
      } else {
        console.error('文件上传失败:', result.errMsg);
      }
    }
    
    return fileIDs;
  } catch (error) {
    console.error('批量上传失败:', error);
    throw error;
  }
}

// 调用函数
uploadMultipleFiles();

3.2 文件下载

文件下载是将云端文件下载到本地的操作:

下载文件到本地

async function downloadFile(fileID) {
  try {
    // 下载文件
    const downloadResult = await uniCloud.downloadFile({
      fileID: fileID
    });
    
    if (downloadResult.success) {
      console.log('文件下载成功:', downloadResult.tempFilePath);
      return downloadResult.tempFilePath;
    } else {
      console.error('文件下载失败:', downloadResult.errMsg);
      throw new Error(downloadResult.errMsg);
    }
  } catch (error) {
    console.error('下载失败:', error);
    throw error;
  }
}

// 调用函数
downloadFile('cloud://service-space-id/images/1234567890.jpg');

下载进度监听

async function downloadFileWithProgress(fileID) {
  try {
    // 下载文件
    const downloadResult = await uniCloud.downloadFile({
      fileID: fileID,
      onProgressUpdate: (progress) => {
        console.log('下载进度:', progress.progress);
        console.log('已下载字节数:', progress.totalBytesWritten);
        console.log('总字节数:', progress.totalBytesExpectedToWrite);
        // 可以在这里更新 UI 显示下载进度
      }
    });
    
    if (downloadResult.success) {
      console.log('文件下载成功:', downloadResult.tempFilePath);
      return downloadResult.tempFilePath;
    } else {
      console.error('文件下载失败:', downloadResult.errMsg);
      throw new Error(downloadResult.errMsg);
    }
  } catch (error) {
    console.error('下载失败:', error);
    throw error;
  }
}

// 调用函数
downloadFileWithProgress('cloud://service-space-id/images/1234567890.jpg');

3.3 文件删除

文件删除是将云端文件删除的操作:

删除单个文件

async function deleteFile(fileID) {
  try {
    // 删除文件
    const deleteResult = await uniCloud.deleteFile({
      fileList: [fileID]
    });
    
    if (deleteResult.success) {
      console.log('文件删除成功:', deleteResult.fileList);
      return deleteResult.fileList;
    } else {
      console.error('文件删除失败:', deleteResult.errMsg);
      throw new Error(deleteResult.errMsg);
    }
  } catch (error) {
    console.error('删除失败:', error);
    throw error;
  }
}

// 调用函数
deleteFile('cloud://service-space-id/images/1234567890.jpg');

批量删除文件

async function deleteMultipleFiles(fileIDs) {
  try {
    // 批量删除文件
    const deleteResult = await uniCloud.deleteFile({
      fileList: fileIDs
    });
    
    if (deleteResult.success) {
      console.log('文件删除成功:', deleteResult.fileList);
      return deleteResult.fileList;
    } else {
      console.error('文件删除失败:', deleteResult.errMsg);
      throw new Error(deleteResult.errMsg);
    }
  } catch (error) {
    console.error('批量删除失败:', error);
    throw error;
  }
}

// 调用函数
deleteMultipleFiles([
  'cloud://service-space-id/images/1234567890.jpg',
  'cloud://service-space-id/images/1234567891.jpg',
  'cloud://service-space-id/images/1234567892.jpg'
]);

3.4 文件访问

上传到云存储的文件可以通过 URL 直接访问:

获取文件访问 URL

function getFileURL(fileID) {
  // 直接使用 fileID 作为文件访问 URL
  return fileID;
}

// 调用函数
const fileURL = getFileURL('cloud://service-space-id/images/1234567890.jpg');
console.log('文件访问 URL:', fileURL);

在 HTML 中使用文件 URL

<!-- 显示图片 -->
<img src="cloud://service-space-id/images/1234567890.jpg" alt="图片" />

<!-- 播放视频 -->
<video src="cloud://service-space-id/videos/1234567890.mp4" controls></video>

<!-- 播放音频 -->
<audio src="cloud://service-space-id/audios/1234567890.mp3" controls></audio>

<!-- 下载文档 -->
<a href="cloud://service-space-id/docs/1234567890.pdf" download>下载文档</a>

3. 云存储的高级操作

3.1 文件元数据管理

文件元数据是指文件的属性信息,如文件名、大小、类型、上传时间等。uniCloud 云存储支持获取和管理文件元数据:

获取文件元数据

async function getFileMetadata(fileID) {
  try {
    // 获取文件元数据
    const metadataResult = await uniCloud.getFileInfo({
      fileList: [fileID]
    });
    
    if (metadataResult.success) {
      console.log('文件元数据:', metadataResult.fileList);
      return metadataResult.fileList[0];
    } else {
      console.error('获取文件元数据失败:', metadataResult.errMsg);
      throw new Error(metadataResult.errMsg);
    }
  } catch (error) {
    console.error('获取文件元数据失败:', error);
    throw error;
  }
}

// 调用函数
getFileMetadata('cloud://service-space-id/images/1234567890.jpg');

3.2 文件权限管理

uniCloud 云存储支持设置文件的访问权限,确保文件安全:

文件权限类型

  • public:公开访问,任何人都可以通过 URL 访问文件
  • private:私有访问,只有通过签名 URL 或云函数才能访问文件

设置文件权限

文件权限的设置通常在云函数中进行,通过 uniCloud 的文件管理 API:

'use strict';
const cloud = uniCloud;

exports.main = async (event, context) => {
  const { fileID, permission } = event;
  
  try {
    // 设置文件权限
    const result = await cloud.updateFile({
      fileID: fileID,
      permissions: permission // 'public' 或 'private'
    });
    
    return {
      code: 0,
      message: '设置文件权限成功',
      data: result
    };
  } catch (error) {
    console.error('设置文件权限失败:', error);
    return {
      code: 500,
      message: '设置文件权限失败',
      error: error.message
    };
  }
};

3.3 文件签名 URL

对于私有文件,可以通过生成签名 URL 来授权访问:

生成签名 URL

签名 URL 的生成通常在云函数中进行,通过 uniCloud 的文件管理 API:

'use strict';
const cloud = uniCloud;

exports.main = async (event, context) => {
  const { fileID, expires } = event;
  
  try {
    // 生成签名 URL
    const result = await cloud.getTempFileURL({
      fileList: [{
        fileID: fileID,
        maxAge: expires || 3600 // 过期时间,单位秒
      }]
    });
    
    return {
      code: 0,
      message: '生成签名 URL 成功',
      data: result.fileList[0]
    };
  } catch (error) {
    console.error('生成签名 URL 失败:', error);
    return {
      code: 500,
      message: '生成签名 URL 失败',
      error: error.message
    };
  }
};

4. 云存储的最佳实践

4.1 文件命名规范

合理的文件命名规范可以提高文件管理的效率:

  • 使用唯一文件名:使用时间戳、随机数等生成唯一文件名,避免文件名冲突
  • 使用目录结构:根据文件类型、用途等组织目录结构,如 images/videos/audios/docs/
  • 使用小写字母:文件名使用小写字母,避免大小写混淆
  • 使用短横线分隔:文件名中的单词使用短横线分隔,如 user-avatar.jpg
  • 包含文件扩展名:文件名包含正确的文件扩展名,如 .jpg.png.mp4.pdf

4.2 文件大小限制

uniCloud 云存储对文件大小有一定限制:

  • 单个文件大小限制:通常为 50MB(具体以 uniCloud 官方文档为准)
  • 上传速度限制:根据网络环境和文件大小不同,上传速度会有所差异
  • 并发上传限制:同时上传的文件数量建议不超过 10 个

4.3 文件类型限制

uniCloud 云存储支持多种文件类型,但也有一些限制:

  • 支持的文件类型:图片、视频、音频、文档等常见文件类型
  • 不支持的文件类型:可执行文件、脚本文件等可能存在安全风险的文件类型

4.4 性能优化

上传优化

  • 文件压缩:上传图片、视频等文件时,先进行压缩,减少文件大小
  • 分片上传:对于大文件,使用分片上传,提高上传成功率
  • 批量上传:使用 Promise.all 进行批量上传,提高上传效率
  • 上传进度:显示上传进度,提升用户体验

下载优化

  • 使用 CDN:利用云存储的 CDN 加速功能,提高文件访问速度
  • 缓存策略:合理设置文件缓存策略,减少重复下载
  • 按需下载:只下载必要的文件,避免过度下载
  • 下载进度:显示下载进度,提升用户体验

存储优化

  • 定期清理:定期清理无用文件,减少存储成本
  • 文件压缩:对大型文本文件进行压缩存储
  • 合理使用存储:根据文件重要性选择合适的存储策略

4.5 安全最佳实践

  • 文件权限:对于敏感文件,设置为私有访问权限
  • 签名 URL:使用签名 URL 控制私有文件的访问权限和过期时间
  • 文件验证:上传文件时验证文件类型和大小,防止恶意文件上传
  • 访问控制:通过云函数控制文件的上传、下载和删除权限
  • 日志记录:记录文件操作日志,便于审计和排查问题

实用案例分析

案例一:开发一个用户头像上传功能

需求分析

开发一个用户头像上传功能,包括选择图片、上传到云存储、获取头像 URL、更新用户头像等功能。

实现步骤

  1. 实现头像选择和上传

客户端代码

async function uploadAvatar() {
  try {
    // 选择图片
    const [chooseResult] = await uni.chooseImage({
      count: 1,
      sizeType: ['compressed'],
      sourceType: ['album', 'camera']
    });
    
    // 上传文件到 uniCloud
    const uploadResult = await uniCloud.uploadFile({
      filePath: chooseResult.tempFilePaths[0],
      cloudPath: 'avatars/' + Date.now() + '.jpg',
      onProgressUpdate: (progress) => {
        console.log('上传进度:', progress.progress);
        // 可以在这里更新 UI 显示上传进度
      }
    });
    
    if (uploadResult.success) {
      console.log('头像上传成功:', uploadResult.fileID);
      return uploadResult.fileID;
    } else {
      console.error('头像上传失败:', uploadResult.errMsg);
      throw new Error(uploadResult.errMsg);
    }
  } catch (error) {
    console.error('上传失败:', error);
    throw error;
  }
}

// 调用函数
uploadAvatar();
  1. 实现更新用户头像

云函数代码

updateAvatar/index.js

'use strict';
const db = uniCloud.database();

exports.main = async (event, context) => {
  const { userId, avatarUrl } = event;
  
  try {
    // 更新用户头像
    const res = await db.collection('users')
      .doc(userId)
      .update({
        avatarUrl: avatarUrl,
        updateTime: new Date()
      });
    
    return {
      code: 0,
      message: '更新头像成功',
      data: {
        updated: res.updated
      }
    };
  } catch (error) {
    console.error('更新头像失败:', error);
    return {
      code: 500,
      message: '更新头像失败',
      error: error.message
    };
  }
};

客户端调用

async function updateUserAvatar(userId, avatarUrl) {
  try {
    const res = await uniCloud.callFunction({
      name: 'updateAvatar',
      data: {
        userId: userId,
        avatarUrl: avatarUrl
      }
    });
    
    if (res.result.code === 0) {
      console.log('更新用户头像成功:', res.result.data);
      return res.result.data;
    } else {
      console.error('更新用户头像失败:', res.result.message);
      throw new Error(res.result.message);
    }
  } catch (error) {
    console.error('调用云函数失败:', error);
    throw error;
  }
}

// 调用函数
async function handleAvatarUpload() {
  try {
    // 上传头像
    const avatarUrl = await uploadAvatar();
    
    // 更新用户头像
    const updateResult = await updateUserAvatar('user-id', avatarUrl);
    
    console.log('头像更新完成:', updateResult);
    // 可以在这里更新 UI 显示新头像
  } catch (error) {
    console.error('头像更新失败:', error);
    // 可以在这里显示错误提示
  }
}

// 调用函数
handleAvatarUpload();
  1. 实现头像显示

客户端代码

<template>
  <view class="avatar-container">
    <image 
      class="avatar" 
      :src="userAvatar || defaultAvatar" 
      mode="aspectFill"
    ></image>
    <button class="upload-btn" @click="handleUpload">上传头像</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      userAvatar: '',
      defaultAvatar: 'https://via.placeholder.com/150'
    };
  },
  mounted() {
    this.getUserAvatar();
  },
  methods: {
    async getUserAvatar() {
      try {
        // 调用云函数获取用户信息
        const res = await uniCloud.callFunction({
          name: 'getUserInfo',
          data: {
            userId: 'user-id'
          }
        });
        
        if (res.result.code === 0) {
          this.userAvatar = res.result.data.user.avatarUrl;
        }
      } catch (error) {
        console.error('获取用户头像失败:', error);
      }
    },
    async handleUpload() {
      try {
        // 上传头像
        const avatarUrl = await this.uploadAvatar();
        
        // 更新用户头像
        await this.updateUserAvatar('user-id', avatarUrl);
        
        // 更新本地头像
        this.userAvatar = avatarUrl;
        
        uni.showToast({
          title: '头像上传成功',
          icon: 'success'
        });
      } catch (error) {
        console.error('头像上传失败:', error);
        uni.showToast({
          title: '头像上传失败',
          icon: 'none'
        });
      }
    },
    async uploadAvatar() {
      const [chooseResult] = await uni.chooseImage({
        count: 1,
        sizeType: ['compressed'],
        sourceType: ['album', 'camera']
      });
      
      const uploadResult = await uniCloud.uploadFile({
        filePath: chooseResult.tempFilePaths[0],
        cloudPath: 'avatars/' + Date.now() + '.jpg'
      });
      
      if (uploadResult.success) {
        return uploadResult.fileID;
      } else {
        throw new Error(uploadResult.errMsg);
      }
    },
    async updateUserAvatar(userId, avatarUrl) {
      const res = await uniCloud.callFunction({
        name: 'updateAvatar',
        data: {
          userId,
          avatarUrl
        }
      });
      
      if (res.result.code !== 0) {
        throw new Error(res.result.message);
      }
    }
  }
};
</script>

<style>
.avatar-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 50rpx;
}

.avatar {
  width: 300rpx;
  height: 300rpx;
  border-radius: 50%;
  margin-bottom: 30rpx;
}

.upload-btn {
  width: 200rpx;
  height: 80rpx;
  line-height: 80rpx;
  text-align: center;
  background-color: #007AFF;
  color: white;
  border-radius: 40rpx;
  font-size: 32rpx;
}
</style>

案例二:开发一个图片上传和管理系统

需求分析

开发一个图片上传和管理系统,包括图片上传、获取图片列表、预览图片、删除图片等功能。

实现步骤

  1. 创建集合

创建以下集合:

  • images:存储图片信息
  1. 设计集合结构

images 集合结构

{
  _id: 'image-id', // 图片 ID,系统自动生成
  fileID: 'cloud://service-space-id/images/1234567890.jpg', // 云存储文件 ID
  url: 'cloud://service-space-id/images/1234567890.jpg', // 图片访问 URL
  name: 'image.jpg', // 图片名称
  size: 102400, // 图片大小(字节)
  type: 'image/jpeg', // 图片类型
  width: 1920, // 图片宽度
  height: 1080, // 图片高度
  userId: 'user-id', // 上传用户 ID
  createTime: new Date() // 上传时间
}
  1. 实现图片上传功能

客户端代码

async function uploadImage() {
  try {
    // 选择图片
    const [chooseResult] = await uni.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera']
    });
    
    const tempFile = chooseResult.tempFiles[0];
    
    // 上传文件到 uniCloud
    const uploadResult = await uniCloud.uploadFile({
      filePath: tempFile.path,
      cloudPath: 'images/' + Date.now() + '.jpg',
      onProgressUpdate: (progress) => {
        console.log('上传进度:', progress.progress);
        // 可以在这里更新 UI 显示上传进度
      }
    });
    
    if (uploadResult.success) {
      console.log('图片上传成功:', uploadResult.fileID);
      
      // 调用云函数记录图片信息
      const recordResult = await uniCloud.callFunction({
        name: 'recordImage',
        data: {
          fileID: uploadResult.fileID,
          url: uploadResult.fileID,
          name: tempFile.name,
          size: tempFile.size,
          type: tempFile.type,
          width: tempFile.width,
          height: tempFile.height,
          userId: 'user-id'
        }
      });
      
      if (recordResult.result.code === 0) {
        console.log('图片记录成功:', recordResult.result.data);
        return recordResult.result.data;
      } else {
        console.error('图片记录失败:', recordResult.result.message);
        throw new Error(recordResult.result.message);
      }
    } else {
      console.error('图片上传失败:', uploadResult.errMsg);
      throw new Error(uploadResult.errMsg);
    }
  } catch (error) {
    console.error('上传失败:', error);
    throw error;
  }
}

// 调用函数
uploadImage();

recordImage/index.js

'use strict';
const db = uniCloud.database();

exports.main = async (event, context) => {
  const { fileID, url, name, size, type, width, height, userId } = event;
  
  try {
    // 记录图片信息
    const res = await db.collection('images').add({
      fileID,
      url,
      name,
      size,
      type,
      width,
      height,
      userId,
      createTime: new Date()
    });
    
    return {
      code: 0,
      message: '记录图片成功',
      data: {
        id: res.id,
        fileID,
        url
      }
    };
  } catch (error) {
    console.error('记录图片失败:', error);
    return {
      code: 500,
      message: '记录图片失败',
      error: error.message
    };
  }
};
  1. 实现获取图片列表功能

客户端代码

async function getImageList(params) {
  try {
    const res = await uniCloud.callFunction({
      name: 'getImageList',
      data: {
        userId: 'user-id',
        page: params.page || 1,
        pageSize: params.pageSize || 10
      }
    });
    
    if (res.result.code === 0) {
      console.log('获取图片列表成功:', res.result.data);
      return res.result.data;
    } else {
      console.error('获取图片列表失败:', res.result.message);
      throw new Error(res.result.message);
    }
  } catch (error) {
    console.error('获取图片列表失败:', error);
    throw error;
  }
}

// 调用函数
getImageList({
  page: 1,
  pageSize: 10
});

getImageList/index.js

'use strict';
const db = uniCloud.database();

exports.main = async (event, context) => {
  const { userId, page = 1, pageSize = 10 } = event;
  
  try {
    const offset = (page - 1) * pageSize;
    
    // 获取图片列表
    const res = await db.collection('images')
      .where({ userId })
      .orderBy('createTime', 'desc')
      .skip(offset)
      .limit(pageSize)
      .get();
    
    // 获取总记录数
    const countRes = await db.collection('images').where({ userId }).count();
    
    return {
      code: 0,
      message: '获取图片列表成功',
      data: {
        images: res.data,
        total: countRes.total,
        page,
        pageSize
      }
    };
  } catch (error) {
    console.error('获取图片列表失败:', error);
    return {
      code: 500,
      message: '获取图片列表失败',
      error: error.message
    };
  }
};
  1. 实现图片预览功能

客户端代码

function previewImage(current, urls) {
  uni.previewImage({
    current: current,
    urls: urls,
    success: (res) => {
      console.log('预览图片成功:', res);
    },
    fail: (err) => {
      console.error('预览图片失败:', err);
    }
  });
}

// 调用函数
previewImage(
  'cloud://service-space-id/images/1234567890.jpg',
  [
    'cloud://service-space-id/images/1234567890.jpg',
    'cloud://service-space-id/images/1234567891.jpg',
    'cloud://service-space-id/images/1234567892.jpg'
  ]
);
  1. 实现图片删除功能

客户端代码

async function deleteImage(id, fileID) {
  try {
    const res = await uniCloud.callFunction({
      name: 'deleteImage',
      data: {
        id: id,
        fileID: fileID
      }
    });
    
    if (res.result.code === 0) {
      console.log('删除图片成功:', res.result.data);
      return res.result.data;
    } else {
      console.error('删除图片失败:', res.result.message);
      throw new Error(res.result.message);
    }
  } catch (error) {
    console.error('删除图片失败:', error);
    throw error;
  }
}

// 调用函数
deleteImage('image-id', 'cloud://service-space-id/images/1234567890.jpg');

deleteImage/index.js

'use strict';
const db = uniCloud.database();
const cloud = uniCloud;

exports.main = async (event, context) => {
  const { id, fileID } = event;
  
  try {
    // 从云存储中删除文件
    await cloud.deleteFile({
      fileList: [fileID]
    });
    
    // 从数据库中删除记录
    await db.collection('images').doc(id).remove();
    
    return {
      code: 0,
      message: '删除图片成功',
      data: {
        id: id,
        fileID: fileID
      }
    };
  } catch (error) {
    console.error('删除图片失败:', error);
    return {
      code: 500,
      message: '删除图片失败',
      error: error.message
    };
  }
};
  1. 实现图片管理页面

客户端代码

<template>
  <view class="image-management">
    <view class="upload-section">
      <button class="upload-btn" @click="handleUpload">上传图片</button>
    </view>
    
    <view class="image-list">
      <view 
        v-for="image in images" 
        :key="image._id" 
        class="image-item"
      >
        <image 
          class="image" 
          :src="image.url" 
          mode="aspectFill"
          @click="handlePreview(image.url)"
        ></image>
        <view class="image-info">
          <text class="image-name">{{ image.name }}</text>
          <text class="image-size">{{ formatSize(image.size) }}</text>
        </view>
        <button 
          class="delete-btn" 
          @click="handleDelete(image._id, image.fileID)"
        >
          删除
        </button>
      </view>
    </view>
    
    <view class="pagination" v-if="total > pageSize">
      <button 
        class="page-btn" 
        @click="handlePrevPage" 
        :disabled="page <= 1"
      >
        上一页
      </button>
      <text class="page-info">{{ page }} / {{ totalPages }}</text>
      <button 
        class="page-btn" 
        @click="handleNextPage" 
        :disabled="page >= totalPages"
      >
        下一页
      </button>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      images: [],
      page: 1,
      pageSize: 10,
      total: 0,
      loading: false
    };
  },
  computed: {
    totalPages() {
      return Math.ceil(this.total / this.pageSize);
    }
  },
  mounted() {
    this.getImageList();
  },
  methods: {
    async getImageList() {
      if (this.loading) return;
      
      this.loading = true;
      try {
        const res = await this.$api.getImageList({
          page: this.page,
          pageSize: this.pageSize
        });
        
        this.images = res.images;
        this.total = res.total;
      } catch (error) {
        console.error('获取图片列表失败:', error);
        uni.showToast({
          title: '获取图片列表失败',
          icon: 'none'
        });
      } finally {
        this.loading = false;
      }
    },
    async handleUpload() {
      try {
        const image = await this.$api.uploadImage();
        uni.showToast({
          title: '图片上传成功',
          icon: 'success'
        });
        // 重新获取图片列表
        this.getImageList();
      } catch (error) {
        console.error('图片上传失败:', error);
        uni.showToast({
          title: '图片上传失败',
          icon: 'none'
        });
      }
    },
    handlePreview(currentUrl) {
      const urls = this.images.map(image => image.url);
      uni.previewImage({
        current: currentUrl,
        urls: urls
      });
    },
    async handleDelete(id, fileID) {
      uni.showModal({
        title: '确认删除',
        content: '确定要删除这张图片吗?',
        success: async (res) => {
          if (res.confirm) {
            try {
              await this.$api.deleteImage(id, fileID);
              uni.showToast({
                title: '图片删除成功',
                icon: 'success'
              });
              // 重新获取图片列表
              this.getImageList();
            } catch (error) {
              console.error('图片删除失败:', error);
              uni.showToast({
                title: '图片删除失败',
                icon: 'none'
              });
            }
          }
        }
      });
    },
    handlePrevPage() {
      if (this.page > 1) {
        this.page--;
        this.getImageList();
      }
    },
    handleNextPage() {
      if (this.page < this.totalPages) {
        this.page++;
        this.getImageList();
      }
    },
    formatSize(size) {
      if (size < 1024) {
        return size + ' B';
      } else if (size < 1024 * 1024) {
        return (size / 1024).toFixed(2) + ' KB';
      } else {
        return (size / (1024 * 1024)).toFixed(2) + ' MB';
      }
    }
  }
};
</script>

<style>
.image-management {
  padding: 20rpx;
}

.upload-section {
  margin-bottom: 30rpx;
}

.upload-btn {
  width: 100%;
  height: 80rpx;
  line-height: 80rpx;
  text-align: center;
  background-color: #007AFF;
  color: white;
  border-radius: 10rpx;
  font-size: 32rpx;
}

.image-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 20rpx;
  margin-bottom: 30rpx;
}

.image-item {
  background-color: #f5f5f5;
  border-radius: 10rpx;
  overflow: hidden;
  position: relative;
}

.image {
  width: 100%;
  height: 200rpx;
}

.image-info {
  padding: 10rpx;
}

.image-name {
  display: block;
  font-size: 28rpx;
  margin-bottom: 5rpx;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.image-size {
  display: block;
  font-size: 24rpx;
  color: #999;
}

.delete-btn {
  position: absolute;
  top: 10rpx;
  right: 10rpx;
  background-color: rgba(255, 0, 0, 0.7);
  color: white;
  border-radius: 15rpx;
  font-size: 24rpx;
  padding: 5rpx 15rpx;
}

.pagination {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: 30rpx;
}

.page-btn {
  width: 120rpx;
  height: 60rpx;
  line-height: 60rpx;
  text-align: center;
  background-color: #f5f5f5;
  border-radius: 30rpx;
  font-size: 28rpx;
  margin: 0 20rpx;
}

.page-btn:disabled {
  opacity: 0.5;
}

.page-info {
  font-size: 28rpx;
  color: #333;
}
</style>

学习目标

通过本章节的学习,您应该能够:

  1. 了解云存储的概念和特点
  2. 掌握云存储的开通与配置方法
  3. 学会云存储的基本操作:文件上传、下载、删除
  4. 掌握云存储的高级操作:文件元数据管理、权限管理、签名 URL
  5. 了解云存储的最佳实践,如文件命名规范、大小限制、类型限制
  6. 掌握云存储的性能优化和安全最佳实践
  7. 能够开发实际的云存储应用,如用户头像上传和图片管理系统

总结与展望

云存储是 uni-app 开发中的重要组成部分,它为开发者提供了一种简单、高效的文件存储和管理方式,无需关心存储服务的搭建和维护。通过本章节的学习,您已经掌握了云存储的开通、配置、基本操作、高级操作、最佳实践、性能优化和安全管理,为您开发文件存储相关的应用奠定了基础。

在后续的学习中,我们将继续探索 uni-app 的高级特性,包括云存储与前端的结合使用、云存储的监控和调试、云存储的迁移和备份等内容,帮助您成为一名更加全面的 uni-app 开发者。

记住,云存储是一种强大的工具,但也需要合理使用。在开发过程中,要注意文件的命名规范、大小限制、类型限制,遵循最佳实践,确保文件的安全存储和高效访问。

« 上一篇 uni-app 云数据库操作 下一篇 » uni-app 云开发实战