鸿蒙OS&UniApp制作支持多图上传的图片选择器:打造高性能移动端上传体验#三方框架 #Uniapp

UniApp制作支持多图上传的图片选择器:打造高性能移动端上传体验

在移动应用开发中,图片上传功能是一个非常常见的需求。本文将详细介绍如何在UniApp框架下实现一个功能完善的多图选择上传组件,并重点关注鸿蒙系统的适配与优化。

一、需求分析

在开发多图上传功能时,我们需要考虑以下几个关键点:

  • 图片选择与预览
  • 图片压缩与质量控制
  • 上传进度管理
  • 失败重试机制
  • 鸿蒙系统特有的媒体能力

二、技术方案设计

2.1 核心技术栈

  • UniApp框架
  • HMS Image Kit
  • Vuex状态管理
  • 自定义组件封装

2.2 功能特性

  1. 支持多选与预览
  2. 智能压缩算法
  3. 批量上传队列
  4. 进度监控
  5. 失败重试机制

三、具体实现

3.1 图片选择器组件

首先,我们实现一个基础的图片选择器组件:

vue 复制代码
<template>
  <view class="image-uploader">
    <!-- 已选图片预览区 -->
    <view class="preview-list">
      <view 
        class="preview-item" 
        v-for="(item, index) in imageList" 
        :key="index"
      >
        <image 
          :src="item.path" 
          mode="aspectFill"
          @tap="previewImage(index)"
        />
        <view 
          class="delete-btn"
          @tap.stop="removeImage(index)"
        >×</view>
        <view 
          class="upload-progress" 
          v-if="item.progress !== 100"
        >
          {{ item.progress }}%
        </view>
      </view>
    </view>
    
    <!-- 选择图片按钮 -->
    <view 
      class="selector"
      @tap="chooseImages"
      v-if="imageList.length < maxCount"
    >
      <text class="iconfont icon-add"></text>
      <text class="tip">点击选择</text>
    </view>
  </view>
</template>

<script>
import { compress } from './utils/imageProcessor'
import { uploadToServer } from './utils/uploader'

export default {
  name: 'ImageUploader',
  props: {
    maxCount: {
      type: Number,
      default: 9
    },
    maxSize: {
      type: Number,
      default: 5 * 1024 * 1024 // 5MB
    }
  },
  data() {
    return {
      imageList: [],
      uploadQueue: []
    }
  },
  methods: {
    async chooseImages() {
      try {
        // #ifdef HARMONY-OS
        const result = await this.chooseImagesHMS()
        // #endif
        
        // #ifndef HARMONY-OS
        const result = await uni.chooseImage({
          count: this.maxCount - this.imageList.length,
          sizeType: ['compressed'],
          sourceType: ['album', 'camera']
        })
        // #endif
        
        this.handleChooseSuccess(result)
      } catch (error) {
        uni.showToast({
          title: '选择图片失败',
          icon: 'none'
        })
      }
    },
    
    // 鸿蒙系统图片选择实现
    async chooseImagesHMS() {
      return new Promise((resolve, reject) => {
        // HMS Image Kit实现
        const imageKit = this.getHMSImageKit()
        imageKit.pickImages({
          maxSelectCount: this.maxCount - this.imageList.length,
          compressFormat: 'JPEG',
          compressQuality: 80
        })
        .then(resolve)
        .catch(reject)
      })
    },
    
    async handleChooseSuccess(result) {
      const images = result.tempFiles || result.tempFilePaths.map(path => ({ path }))
      
      // 处理每张图片
      for (const image of images) {
        // 压缩图片
        const compressed = await compress(image.path, {
          quality: 80,
          maxWidth: 1920,
          maxHeight: 1920
        })
        
        // 添加到列表
        this.imageList.push({
          path: compressed.path,
          size: compressed.size,
          progress: 0,
          status: 'waiting'
        })
      }
      
      // 开始上传队列
      this.startUpload()
    },
    
    async startUpload() {
      const waiting = this.imageList.filter(img => img.status === 'waiting')
      
      for (const image of waiting) {
        try {
          image.status = 'uploading'
          
          await uploadToServer(image.path, {
            onProgress: (progress) => {
              image.progress = progress
            }
          })
          
          image.status = 'success'
          image.progress = 100
          
          this.$emit('upload-success', image)
        } catch (error) {
          image.status = 'failed'
          this.$emit('upload-error', { image, error })
        }
      }
    },
    
    removeImage(index) {
      this.imageList.splice(index, 1)
      this.$emit('remove', index)
    },
    
    previewImage(index) {
      const urls = this.imageList.map(img => img.path)
      uni.previewImage({
        urls,
        current: index
      })
    }
  }
}
</script>

<style lang="scss">
.image-uploader {
  display: flex;
  flex-wrap: wrap;
  padding: 20rpx;
  
  .preview-list {
    display: flex;
    flex-wrap: wrap;
  }
  
  .preview-item {
    position: relative;
    width: 200rpx;
    height: 200rpx;
    margin: 10rpx;
    
    image {
      width: 100%;
      height: 100%;
      border-radius: 8rpx;
    }
    
    .delete-btn {
      position: absolute;
      top: -20rpx;
      right: -20rpx;
      width: 40rpx;
      height: 40rpx;
      line-height: 40rpx;
      text-align: center;
      background: rgba(0, 0, 0, 0.5);
      color: #fff;
      border-radius: 50%;
    }
    
    .upload-progress {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      height: 40rpx;
      line-height: 40rpx;
      text-align: center;
      background: rgba(0, 0, 0, 0.5);
      color: #fff;
      font-size: 24rpx;
    }
  }
  
  .selector {
    width: 200rpx;
    height: 200rpx;
    margin: 10rpx;
    border: 2rpx dashed #ddd;
    border-radius: 8rpx;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    
    .icon-add {
      font-size: 60rpx;
      color: #999;
    }
    
    .tip {
      margin-top: 10rpx;
      font-size: 24rpx;
      color: #999;
    }
  }
}
</style>

3.2 图片压缩处理

为了优化上传性能,我们实现了智能压缩功能:

javascript 复制代码
// utils/imageProcessor.js
export const compress = async (path, options = {}) => {
  const {
    quality = 80,
    maxWidth = 1920,
    maxHeight = 1920
  } = options
  
  // 获取图片信息
  const imageInfo = await uni.getImageInfo({
    src: path
  })
  
  // 计算压缩后的尺寸
  let { width, height } = imageInfo
  const ratio = Math.min(
    maxWidth / width,
    maxHeight / height,
    1
  )
  
  width = Math.floor(width * ratio)
  height = Math.floor(height * ratio)
  
  // 压缩图片
  return new Promise((resolve, reject) => {
    // #ifdef HARMONY-OS
    // 使用HMS Image Kit压缩
    const imageKit = getHMSImageKit()
    imageKit.compressImage({
      uri: path,
      format: 'JPEG',
      quality,
      targetWidth: width,
      targetHeight: height
    })
    .then(resolve)
    .catch(reject)
    // #endif
    
    // #ifndef HARMONY-OS
    uni.compressImage({
      src: path,
      quality,
      width,
      height,
      success: resolve,
      fail: reject
    })
    // #endif
  })
}

3.3 上传队列管理

实现一个可靠的上传队列管理器:

javascript 复制代码
// utils/uploader.js
class UploadQueue {
  constructor(options = {}) {
    this.queue = []
    this.concurrent = options.concurrent || 3
    this.running = 0
    this.retryTimes = options.retryTimes || 3
    this.retryDelay = options.retryDelay || 1000
  }
  
  add(task) {
    this.queue.push({
      ...task,
      retryCount: 0
    })
    this.check()
  }
  
  async check() {
    if (this.running >= this.concurrent) return
    
    const task = this.queue.find(t => !t.isRunning)
    if (!task) return
    
    this.running++
    task.isRunning = true
    
    try {
      await this.runTask(task)
    } catch (error) {
      if (task.retryCount < this.retryTimes) {
        task.retryCount++
        task.isRunning = false
        await this.delay(this.retryDelay)
        this.check()
      } else {
        task.onError && task.onError(error)
      }
    }
    
    this.running--
    this.check()
  }
  
  async runTask(task) {
    const formData = new FormData()
    formData.append('file', task.file)
    
    const response = await fetch(task.url, {
      method: 'POST',
      body: formData,
      headers: task.headers
    })
    
    if (!response.ok) {
      throw new Error(`Upload failed: ${response.statusText}`)
    }
    
    const result = await response.json()
    task.onSuccess && task.onSuccess(result)
  }
  
  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
  }
}

export const uploadToServer = (filePath, options = {}) => {
  return new Promise((resolve, reject) => {
    const uploader = new UploadQueue()
    
    uploader.add({
      file: filePath,
      url: options.url || '/api/upload',
      headers: options.headers,
      onProgress: options.onProgress,
      onSuccess: resolve,
      onError: reject
    })
  })
}

四、鸿蒙系统适配

4.1 HMS Image Kit集成

在鸿蒙系统上,我们利用HMS Image Kit提供的能力:

javascript 复制代码
export class HMSImageManager {
  constructor() {
    this.imageKit = null
  }
  
  async init() {
    try {
      // 初始化HMS Image Kit
      const hms = await this.getHMSCore()
      this.imageKit = hms.imageKit({
        compressFormat: 'JPEG',
        compressQuality: 80
      })
      return true
    } catch (error) {
      console.error('HMS Image Kit初始化失败:', error)
      return false
    }
  }
  
  async pickImages(options) {
    if (!this.imageKit) await this.init()
    
    return this.imageKit.pickImages({
      ...options,
      mediaType: ['image/jpeg', 'image/png'],
      selectionMode: 'multiple'
    })
  }
  
  async compressImage(options) {
    if (!this.imageKit) await this.init()
    
    return this.imageKit.compressImage(options)
  }
}

4.2 性能优化

针对鸿蒙系统的特点,我们进行了一些性能优化:

javascript 复制代码
const optimizeForHMS = {
  // 图片缓存策略
  cacheStrategy: {
    mode: 'memory_first',
    maxMemorySize: 20 * 1024 * 1024, // 20MB
    maxDiskSize: 100 * 1024 * 1024   // 100MB
  },
  
  // 预加载配置
  preloadConfig: {
    preloadCount: 3,
    preloadDistance: 2
  },
  
  // 内存管理
  memoryManagement: {
    clearThreshold: 0.8,
    autoRelease: true
  }
}

五、实践总结

通过以上实现,我们的图片上传组件具备以下特点:

  1. 支持多图选择与预览
  2. 智能压缩,优化传输性能
  3. 可靠的上传队列管理
  4. 完善的失败重试机制
  5. 鸿蒙系统深度适配

在实际应用中,我们还可以根据具体需求进行以下优化:

  • 自定义图片裁剪功能
  • 添加水印处理
  • 优化大图片加载
  • 实现拖拽排序

六、未来展望

随着鸿蒙生态的发展,我们可以期待:

  1. 更强大的HMS Image Kit功能
  2. 更优秀的图像处理能力
  3. 更完善的媒体管理接口
  4. 更多创新的交互方式

结语

图片上传是一个看似简单但实际涉及诸多技术点的功能,需要我们在性能、体验和可靠性之间找到平衡。通过本文的实践,相信大家已经掌握了实现一个专业图片上传组件的核心要点。在实际开发中,还需要根据具体场景进行调整和优化。

希望本文对大家有所帮助,也欢迎在评论区分享你的开发经验!

相关推荐
Aiden Targaryen15 分钟前
Windows/MacOS WebStorm/IDEA 中开发 Uni-App 配置
java·uni-app·webstorm
想要飞翔的pig1 小时前
uniapp+vue3页面滚动加载数据
前端·vue.js·uni-app
会功夫的李白1 小时前
uniapp自动构建pages.json的vite插件
前端·uni-app·vite
TE-茶叶蛋2 小时前
Uniapp、Flutter 和 React Native 全面对比
flutter·react native·uni-app
落叶挽歌5 小时前
鸿蒙ArkUI体验:Hexo博客客户端开发心得
华为·harmonyos
特立独行的猫a5 小时前
uni-app 开发HarmonyOS的鸿蒙影视项目分享:从实战案例到开源后台
uni-app·开源·harmonyos·鸿蒙·影视
交叉编译之王 hahaha7 小时前
RK3568-鸿蒙5.1镜像烧录与调试
华为·harmonyos
Raink老师7 小时前
鸿蒙页面布局入门
华为·harmonyos·鸿蒙·移动端布局
七七小报8 小时前
uniapp-商城-61-后台 新增商品(添加商品到数据库)
uni-app
hbcui19848 小时前
uni-app x正式支持鸿蒙原生应用开发
uni-app·harmonyos·uni-app x