0
点赞
收藏
分享

微信扫一扫

uni-app开发微信小程序,实现文件上传和下载

UniApp作为一款跨平台的移动应用开发框架,也提供了相应的接口,方便开发者实现文件的下载与上传功能。本文将介绍如何配置与使用UniApp框架中的文件下载与上传功能。

  1. 文件上传
  • 通过 uni.chooseMessageFile 选择本地文件
  • 使用 uni.uploadFile 上传文件到服务器
  • 实时显示上传进度
  • 处理上传成功和失败的情况
  1. 文件下载
  • 输入文件 URL 进行下载
  • 使用 uni.downloadFile 下载文件
  • 实时显示下载进度
  • 下载成功后可直接打开文件
  1. 工具封装
  • 封装了通用的请求工具
  • 提供了文件上传下载的 API

index.vue

<template>
  <view class="container">
    <view class="section">
      <text class="title">文件上传</text>
      <button class="select-btn" @click="selectFile">选择文件</button>
      <view class="file-info" v-if="selectedFile">
        <text class="file-name">{{ selectedFile.name }}</text>
        <text class="file-size">{{ selectedFile.size }}</text>
      </view>
      <button class="upload-btn" @click="uploadSelectedFile" :disabled="!selectedFile">上传文件</button>
      <view class="progress-container" v-if="uploading">
        <view class="progress-bar" :style="{ width: uploadProgress + '%' }"></view>
        <text class="progress-text">{{ uploadProgress }}%</text>
      </view>
      <view class="result" v-if="uploadResult">
        <text class="success-text">上传成功</text>
        <text class="file-url">文件地址:{{ uploadResult.url }}</text>
      </view>
      <view class="error" v-if="uploadError">
        <text class="error-text">上传失败:{{ uploadError }}</text>
      </view>
    </view>

    <view class="section">
      <text class="title">文件下载</text>
      <view class="input-container">
        <input 
          class="url-input" 
          type="text" 
          placeholder="请输入文件URL" 
          v-model="downloadUrl"
        />
        <button class="download-btn" @click="startDownload" :disabled="!downloadUrl">下载文件</button>
      </view>
      <view class="progress-container" v-if="downloading">
        <view class="progress-bar" :style="{ width: downloadProgress + '%' }"></view>
        <text class="progress-text">{{ downloadProgress }}%</text>
      </view>
      <view class="result" v-if="downloadSuccess">
        <text class="success-text">下载成功</text>
        <button class="open-btn" @click="openDownloadedFile">打开文件</button>
      </view>
      <view class="error" v-if="downloadError">
        <text class="error-text">下载失败:{{ downloadError }}</text>
      </view>
    </view>
  </view>
</template>

<script>
import { uploadApi, downloadApi } from './api';

export default {
  data() {
    return {
      selectedFile: null,
      uploading: false,
      uploadProgress: 0,
      uploadResult: null,
      uploadError: null,
      downloadUrl: '',
      downloading: false,
      downloadProgress: 0,
      downloadSuccess: false,
      downloadError: null,
      downloadedFilePath: ''
    };
  },
  methods: {
    // 选择文件
    selectFile() {
      uni.chooseMessageFile({
        count: 1,
        type: 'all',
        success: (res) => {
          const tempFile = res.tempFiles[0];
          this.selectedFile = {
            path: tempFile.path,
            name: tempFile.name,
            size: this.formatSize(tempFile.size)
          };
          this.uploadError = null;
          this.uploadResult = null;
        },
        fail: (err) => {
          this.uploadError = err.errMsg;
        }
      });
    },
    
    // 上传选中的文件
    uploadSelectedFile() {
      if (!this.selectedFile) return;
      
      this.uploading = true;
      this.uploadProgress = 0;
      this.uploadError = null;
      this.uploadResult = null;
      
      try {
        const uploadTask = uploadApi(
          this.selectedFile.path,
          this.selectedFile.name,
          (progress) => {
            this.uploadProgress = progress;
          }
        );
        
        uploadTask.then((res) => {
          this.uploading = false;
          this.uploadResult = res;
          uni.showToast({
            title: '上传成功',
            icon: 'success'
          });
        }).catch((err) => {
          this.uploading = false;
          this.uploadError = err.message;
          uni.showToast({
            title: '上传失败',
            icon: 'none'
          });
        });
      } catch (err) {
        this.uploading = false;
        this.uploadError = err.message;
        uni.showToast({
          title: '上传失败',
          icon: 'none'
        });
      }
    },
    
    // 开始下载文件
    startDownload() {
      if (!this.downloadUrl) return;
      
      this.downloading = true;
      this.downloadProgress = 0;
      this.downloadSuccess = false;
      this.downloadError = null;
      
      try {
        const downloadTask = downloadApi(
          this.downloadUrl,
          (progress) => {
            this.downloadProgress = progress;
          }
        );
        
        downloadTask.then((tempFilePath) => {
          this.downloading = false;
          this.downloadSuccess = true;
          this.downloadedFilePath = tempFilePath;
          uni.showToast({
            title: '下载成功',
            icon: 'success'
          });
        }).catch((err) => {
          this.downloading = false;
          this.downloadError = err.message;
          uni.showToast({
            title: '下载失败',
            icon: 'none'
          });
        });
      } catch (err) {
        this.downloading = false;
        this.downloadError = err.message;
        uni.showToast({
          title: '下载失败',
          icon: 'none'
        });
      }
    },
    
    // 打开下载的文件
    openDownloadedFile() {
      if (!this.downloadedFilePath) return;
      
      uni.openDocument({
        filePath: this.downloadedFilePath,
        success: () => {
          console.log('文件打开成功');
        },
        fail: (err) => {
          uni.showToast({
            title: '打开文件失败',
            icon: 'none'
          });
          console.error('打开文件失败:', err);
        }
      });
    },
    
    // 格式化文件大小
    formatSize(bytes) {
      if (bytes === 0) return '0 B';
      
      const k = 1024;
      const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }
  }
};
</script>

<style>
.container {
  padding: 20rpx;
}

.section {
  margin-bottom: 40rpx;
  background-color: #fff;
  border-radius: 10rpx;
  padding: 20rpx;
  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}

.title {
  font-size: 36rpx;
  font-weight: bold;
  margin-bottom: 20rpx;
  color: #333;
  display: block;
}

.select-btn, .upload-btn, .download-btn, .open-btn {
  width: 100%;
  height: 80rpx;
  line-height: 80rpx;
  text-align: center;
  background-color: #007AFF;
  color: #fff;
  border-radius: 8rpx;
  margin-bottom: 20rpx;
  font-size: 28rpx;
}

.select-btn[disabled], .upload-btn[disabled], .download-btn[disabled] {
  background-color: #CCCCCC;
}

.file-info {
  display: flex;
  justify-content: space-between;
  margin-bottom: 20rpx;
  font-size: 28rpx;
  color: #666;
}

.progress-container {
  width: 100%;
  height: 20rpx;
  background-color: #f3f3f3;
  border-radius: 10rpx;
  margin-bottom: 20rpx;
  position: relative;
}

.progress-bar {
  height: 100%;
  background-color: #007AFF;
  border-radius: 10rpx;
  transition: width 0.3s ease;
}

.progress-text {
  position: absolute;
  right: 0;
  top: -30rpx;
  font-size: 24rpx;
  color: #666;
}

.result, .error {
  margin-bottom: 20rpx;
}

.success-text {
  color: #4CD964;
  font-size: 28rpx;
  display: block;
  margin-bottom: 10rpx;
}

.error-text {
  color: #FF3B30;
  font-size: 28rpx;
  display: block;
  margin-bottom: 10rpx;
}

.file-url {
  color: #007AFF;
  font-size: 24rpx;
  word-break: break-all;
  display: block;
}

.input-container {
  margin-bottom: 20rpx;
}

.url-input {
  width: 100%;
  height: 80rpx;
  line-height: 80rpx;
  border: 1rpx solid #ddd;
  border-radius: 8rpx;
  padding: 0 20rpx;
  margin-bottom: 20rpx;
  font-size: 28rpx;
  box-sizing: border-box;
}
</style>

api.js

// pages/upload-download/api.js
import { uploadFile, downloadFile, getUploadProgress, getDownloadProgress } from '@/utils/request';

// 文件上传API
export function uploadApi(filePath, fileName, onProgress) {
  const uploadTask = uploadFile('/upload', filePath, fileName);
  
  // 如果有进度回调,监听进度
  if (typeof onProgress === 'function') {
    getUploadProgress(uploadTask, onProgress);
  }
  
  return uploadTask;
}

// 文件下载API
export function downloadApi(fileUrl, onProgress) {
  const downloadTask = downloadFile(fileUrl);
  
  // 如果有进度回调,监听进度
  if (typeof onProgress === 'function') {
    getDownloadProgress(downloadTask, onProgress);
  }
  
  return downloadTask;
}

request.js

// utils/request.js
import uni from '@dcloudio/uni-app';

/**
 * 上传文件
 * @param {string} url 上传地址
 * @param {string} filePath 文件路径
 * @param {string} name 文件字段名
 * @param {object} [data] 额外数据
 * @returns {Promise} 上传任务
 */
export function uploadFile(url, filePath, name, data = {}) {
  return uni.uploadFile({
    url: url,
    filePath: filePath,
    name: name,
    formData: data,
    success: (res) => {
      if (res.statusCode === 200) {
        return JSON.parse(res.data);
      } else {
        throw new Error(`上传失败,状态码:${res.statusCode}`);
      }
    },
    fail: (err) => {
      throw new Error(`上传失败:${err.errMsg}`);
    }
  });
}

/**
 * 下载文件
 * @param {string} url 文件地址
 * @returns {Promise} 下载任务
 */
export function downloadFile(url) {
  return uni.downloadFile({
    url: url,
    success: (res) => {
      if (res.statusCode === 200) {
        return res.tempFilePath;
      } else {
        throw new Error(`下载失败,状态码:${res.statusCode}`);
      }
    },
    fail: (err) => {
      throw new Error(`下载失败:${err.errMsg}`);
    }
  });
}

/**
 * 获取上传进度
 * @param {object} uploadTask 上传任务
 * @param {function} callback 进度回调
 */
export function getUploadProgress(uploadTask, callback) {
  uploadTask.onProgressUpdate((res) => {
    callback(res.progress);
  });
}

/**
 * 获取下载进度
 * @param {object} downloadTask 下载任务
 * @param {function} callback 进度回调
 */
export function getDownloadProgress(downloadTask, callback) {
  downloadTask.onProgressUpdate((res) => {
    callback(res.progress);
  });
}

注意:

  1. 替换代码中的 API 地址为你实际的后端接口
  2. 根据后端要求调整上传参数
  3. 处理可能的权限问题(如需要在 manifest.json 中配置文件读写权限)
举报

相关推荐

0 条评论