flutter、微信小程序uni-app、web(avue)上传图片资源到oss

一、微信小程序上传图片资源到oss

微信小程序直传实践
使用的是uview的u-upload组件
1、表单页面
a、页面

	<u-upload :fileList="fileList" name="1" multiple :maxCount="3" @delete="deleteFun" @afterRead="afterRead"
					:previewFullImage="true"></u-upload>

b、主要js

async afterRead(event) {
				let fileArr = event.file;
				let fileList = this.fileList.concat(event.file);
				this.fileList = fileList; //显示在页面上的图片列表
				for (let i = 0; i < fileArr.length; i++) {
					let data = await initClient(); //获取需要的上传的参数,文件在后面
					const host = 'http://阿里后台配置的桶名.oss-cn-shenzhen.aliyuncs.com';
					const signature = data.signature;
					const ossAccessKeyId = data.OSSAccessKeyId;
					const policy = data.policy;
					const securityToken = data.securityToken;

					let tempFile = fileArr[i];
					let filePath =tempFile.url;
					
					let date = new Date();
					let year = date.getFullYear();
					let month = date.getMonth() + 1;
					let month2 = month < 10 ? "0" + month : month;
					let date2 = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
					let name = "";
					if (!tempFile.name) {
						let path = tempFile.url;
						let indexOf = path.lastIndexOf("/");
						name = path.substring(indexOf + 1);
					} else {
						name = tempFile.name;
					}
					let indexOf = name.lastIndexOf(".");
					let extension = name.substring(indexOf + 1);

					const key =
						`public/car/${tenantId}/${year}/${month2}/${date2}/${uuidv4()}.${extension}`;
					//const key = '/public/car/000000/2023/01/12/'+res.tempFiles[0].name;  //多了一根斜杠报错:InvalidObjectName
					uni.uploadFile({
						url: host, // 开发者服务器的URL。
						filePath: filePath, // 待上传文件的文件路径。
						name: 'file', // 必须填file。
						formData: {
							key,//传到服务器的地址,切记千万不要有斜杠
							policy,
							'success_action_status': '200', //让服务端返回200,不然,默认会返回204
							OSSAccessKeyId: ossAccessKeyId,
							signature,
							'x-oss-security-token': securityToken // 使用STS签名时必传。
						},
						success: (res2) => {
							if (res2.statusCode === 200) {
								let link = host + "/" + key;
								//调取后台接口
								saveAttachAPI({
									link: link,
									domainUrl: host,
									name: key,
									originalName: name,
									extension: extension,
									attachSize: tempFile.size
								}).then(([err, attach_data]) => {
									if (err) return;
									let params = {
										attachId: attach_data,
										link: link,
										originalName:name,
										attachCategory:1,
										businessType:1
									};
									this.requestFileList.push(params);//保存需要保存到后台的是数据
								})
							}
						},
						fail: err => {
							console.log(err);
						}
					});
				}
			}

		}

ossClient.js

const MpUploadOssHelper = require("./uploadOssHelper.js");

import {
	getStsAPI
} from "@/api/api/my.js"
import {
	getNowTime
} from "@/utils/utils.js"
export const initClient = async () => {
	let value = uni.getStorageSync('ossConfig'); //获取缓存里面储存的oss配置数据
	//对比时间和缓存里面是否有值,如果过期,需要重新获取sts参数
	if (value && value.expiration > getNowTime()) {
		return value;
	} else {
		let [err, data] = await getStsAPI();
		const mpHelper = new MpUploadOssHelper({
			// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户并授权。
			accessKeyId: data.accessKeyId,
			accessKeySecret: data.accessKeySecret,
		});
		// 生成参数。
		const params = mpHelper.createUploadParams();
		params.securityToken = data.securityToken;
		params.expiration = data.expiration;
		//保存oss配置参数
		uni.setStorage({
			key: 'ossConfig',
			data: params,
		});
		return params;
	}

}

uploadOssHelper.js(这个是从oss文档里面copy出来的)

const crypto = require("crypto-js");

class MpUploadOssHelper {
  constructor(options) {
    this.accessKeyId = options.accessKeyId;
    this.accessKeySecret = options.accessKeySecret;
    // 限制参数的生效时间,单位为小时,默认值为1。
    this.timeout = options.timeout || 1; 
    // 限制上传文件的大小,单位为MB,默认值为10。
    this.maxSize = options.maxSize || 10;
  }

  createUploadParams() {
    const policy = this.getPolicyBase64();
    const signature = this.signature(policy);
    return {
      OSSAccessKeyId: this.accessKeyId,
      policy: policy,
      signature: signature,
    };
  }

  getPolicyBase64() {
    let date = new Date();
    // 设置policy过期时间。
    date.setHours(date.getHours() + this.timeout);
    let srcT = date.toISOString();
    const policyText = {
      expiration: srcT,
      conditions: [
        // 限制上传文件大小。
        ["content-length-range", 0, this.maxSize * 1024 * 1024],
      ],
    };
    const buffer = Buffer.from(JSON.stringify(policyText));
    return buffer.toString("base64");
  }

  signature(policy) {
    return crypto.enc.Base64.stringify(
      crypto.HmacSHA1(policy, this.accessKeySecret)
    );
  }
}

module.exports = MpUploadOssHelper;

二、flutter上传图片资源到OSS

利用组件: flutter_oss_aliyun
flutter_oss_aliyun
页面初始化的时候,调用此方法

  static initOss() async {
    String authorization =
        "${ProjectConfig.clientId}:${ProjectConfig.clientSecret}";
    String base64Authorization = base64Encode(utf8.encode(authorization));
    var prefs = await SharedPreferences.getInstance();
    Client2.init(
        stsUrl:
            '${ProjectConfig.baseUrl}/blade-resource/oss/endpoint/get-acs-param-ali',
        stsHeader: {
          'Authorization': 'Basic ${base64Authorization}',
          'Blade-Auth': 'bearer ${prefs.getString("accessToken")}',
          'Content-Type': 'application/json'
        },
        ossEndpoint: 'oss-cn-shenzhen.aliyuncs.com',
        bucketName: 'bucketName');
  }

上传文件调用此方法:

  XFile? image = await _picker.pickImage(source: ImageSource.gallery,imageQuality: 40);
  //获取文件
   var file = await image?.readAsBytes();      
   //获取文件大小                                                
  int? size =
       file?.lengthInBytes;
   String uploadPath='upload/${ProjectConfig.tenantId}/${FunUtils.getNowDate()}/${uuid.v1()}.$extension';
                                                         
await Client2()
        .putObject(
  file!,//字节文件
  uploadPath,//上传到服务器上的地址
);

三、web(avue)上传图片资源到OSS

main.js

//oss配置
Vue.prototype.aliConfig = {
  stsUrl:'/api/blade-resource/oss/endpoint/get-acs-param-ali', // 获取sts服务
  region:'oss-cn-shenzhen',
  bucket:'bucketName',
  tenantId:"000000",//租户id
  system:"car",//系统类型
  rsUrl:"/api/blade-resource/attach/save" //保存到后台的地址
};

修改avue上传模块源码
avue/packages/element-ui/upload/index.vue
修改源码对比
flutter、微信小程序uni-app、web(avue)上传图片资源到oss_第1张图片

<template>
  <div :class="b()"
       v-loading.lock="loading">
    <el-upload :class="b({'list':listType=='picture-img','upload':disabled})"
               @click.native="handleClick"
               :action="action"
               :on-remove="handleRemove"
               :accept="acceptList"
               :before-remove="beforeRemove"
               :multiple="multiple"
               :on-preview="handlePreview"
               :limit="limit"
               :http-request="httpRequest"
               :drag="drag"
               :readonly="readonly"
               :show-file-list="isPictureImg?false:showFileList"
               :list-type="listType"
               :on-change="handleFileChange"
               :on-exceed="handleExceed"
               :disabled="disabled"
               :file-list="fileList">
      <template v-if="listType=='picture-card'">
        <i class="el-icon-plus"></i>
      </template>
      <template v-else-if="listType=='picture-img'">
        <el-image v-if="imgUrl"
                  :src="imgUrl"
                  :preview-src-list="[imgUrl]"
                  v-bind="allParams"
                  @mouseover="menu=true"
                  :class="b('avatar')"></el-image>
        <i v-else
           class="el-icon-plus"
           :class="b('icon')"></i>
        <div class="el-upload-list__item-actions"
             :class="b('menu')"
             v-if="menu"
             @mouseover="menu=true"
             @mouseout="menu=false"
             @click.stop="()=>{return false}">
          <i class="el-icon-zoom-in"
             @click.stop="handlePreview({url:imgUrl})"></i>
          <i class="el-icon-delete"
             v-if="!disabled"
             @click.stop="handleDelete(imgUrl)"></i>
        </div>
      </template>
      <template v-else-if="drag">
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">
          将文件拖到此处,或
          <em>点击上传</em>
        </div>
      </template>
      <template v-else>
        <el-button size="small"
                   type="primary">点击上传</el-button>
      </template>
      <div slot="tip"
           class="el-upload__tip">{{tip}}</div>
    </el-upload>
    <el-dialog append-to-body
               :class="b('dialog')"
               top="0"
               :modal-append-to-body="true"
               :visible.sync="dialogVisible">
      <img v-if="typeList.img.test(dialogUrl)"
           :src="dialogUrl"
           style="max-width:100%"
           alt>
      <video v-else-if="typeList.video.test(dialogUrl)"
             controls="controls"
             style="max-width:100%"
             :src="dialogUrl"></video>
    </el-dialog>
  </div>
</template>

<script>
import create from "core/create";
import props from "../../core/common/props.js";
import event from "../../core/common/event.js";
import { getObjValue } from "utils/util";
import { detailImg } from "plugin/canvas/";
import { getToken } from "plugin/qiniu/";
import { getClient } from "plugin/ali/";
import packages from "core/packages";
export default create({
  name: "upload",
  mixins: [props(), event()],
  data () {
    return {
      menu: false,
      loading: false,
      typeList: {
        img: /(\.(gif|jpg|jpeg|png|GIF|JPG|PNG))|(data:image)/,
        video: /\.(swf|avi|flv|mpg|rm|mov|wav|asf|3gp|mkv|rmvb|ogg|mp4)/
      },
      dialogUrl: "",
      dialogType: true,
      dialogVisible: false,
      text: [],
      file: {}
    };
  },
  props: {
    data: {
      type: Object,
      default: () => {
        return {}
      }
    },
    preview: {
      type: Object,
      default: () => { }
    },
    value: {},
    onRemove: Function,
    showFileList: {
      type: Boolean,
      default: true
    },
    oss: {
      type: String
    },
    limit: {
      type: Number,
      default: 10
    },
    headers: {
      type: Object,
      default: () => {
        return {}
      }
    },
    accept: {
      type: [String, Array],
      default: ""
    },
    canvasOption: {
      type: Object,
      default: () => {
        return {};
      }
    },
    fileSize: {
      type: Number
    },
    drag: {
      type: Boolean,
      default: false
    },
    loadText: {
      type: String,
      default: "文件上传中,请稍等"
    },
    action: {
      type: String,
      default: ""
    },
    uploadBefore: Function,
    uploadAfter: Function,
    uploadDelete: Function,
    uploadPreview: Function,
    uploadError: Function
  },
  computed: {
    acceptList () {
      if (Array.isArray(this.accept)) {
        return this.accept.join(',')
      }
      return this.accept
    },
    homeUrl () {
      return this.propsHttp.home || ''
    },
    allParams () {
      if (this.typeList.video.test(this.imgUrl)) {
        return Object.assign({
          is: 'video'
        }, this.params)
      }
      return this.params
    },
    fileName () {
      return this.propsHttp.fileName || 'file'
    },
    isAliOss () {
      return this.oss === "ali";
    },
    isQiniuOss () {
      return this.oss === "qiniu";
    },
    isPictureImg () {
      return this.listType === "picture-img";
    },
    //单个头像图片
    imgUrl () {
      if (!this.validatenull(this.text)) {
        return this.homeUrl + this.text[0];
      }
    },
    fileList () {
      let list = [];
      const flag = this.isArray || this.isString;
      (this.text || []).forEach((ele, index) => {
        if (ele) {
          let name;
          //处理单个url链接取最后为label
          if (flag) {
            let i = ele.lastIndexOf('/');
            name = ele.substring(i + 1);
          }
          list.push({
            uid: index + '',
            status: 'done',
            name: flag ? name : ele[this.labelKey],
            url: this.homeUrl + (flag ? ele : ele[this.valueKey])
          });
        }
      });
      return list;
    }
  },
  created () { },
  watch: {},
  mounted () { },
  methods: {
    handleClick () {
      if (typeof this.click === "function")
        this.click({ value: this.text, column: this.column });
    },
    handleFileChange (file, fileList) {
      fileList.splice(fileList.length - 1, 1);
      if (typeof this.change === "function")
        this.change({ value: this.text, column: this.column });
    },
    handleSuccess (file) {
      if (this.isArray || this.isString) {
        this.text.push(file[this.urlKey]);
      } else if (this.isPictureImg) {
        this.text.splice(0, 1, file[this.urlKey])
      } else {
        let obj = {};
        obj[this.labelKey] = file[this.nameKey];
        obj[this.valueKey] = file[this.urlKey];
        this.text.push(obj);
      }
      this.$message.success("上传成功");
    },
    handleRemove (file, fileList) {
      this.onRemove && this.onRemove(file, fileList);
      this.delete(file);
      this.$message.success("删除成功");
    },
    handleError (error) {
      if (typeof this.uploadError === "function") {
        this.uploadError(error, this.column)
      }
    },
    delete (file) {
      if (this.isArray || this.isString) {
        (this.text || []).forEach((ele, index) => {
          if (ele === file.url) this.text.splice(index, 1);
        });
      } else {
        (this.text || []).forEach((ele, index) => {
          if (ele[this.valueKey] === file.url) this.text.splice(index, 1);
        });
      }
    },
    show (data) {
      this.loading = false;
      this.handleSuccess(data);
    },
    hide (msg) {
      this.loading = false;
      this.handleError(msg);
    },
    httpRequest (config) {
      this.loading = true;
      let file = config.file;
      const accept = file.type;
      const fileSize = file.size;
      this.file = config.file;
      let acceptList = this.acceptList;
      if (!Array.isArray(acceptList) && !this.validatenull(acceptList)) {
        acceptList = acceptList.split(',')
      }
      if (!this.validatenull(acceptList) && !acceptList.includes(accept)) {
        this.hide("文件类型不符合");
        return;
      }
      if (!this.validatenull(fileSize) && fileSize > this.fileSize) {
        this.hide("文件太大不符合");
        return;
      }
      const headers = Object.assign(this.headers, { "Content-Type": "multipart/form-data" });
      //oss配置属性
      let oss_config = {};
      let client = {};
      let param = new FormData();
      //附加属性
      for (let o in this.data) {
        param.append(o, this.data[o]);
      }
      const done = () => {
        let url = this.action;
        const callack = async (newFile) => {
          const uploadfile = newFile || file;
          param.append(this.fileName, uploadfile);
          //七牛云oss存储
          if (this.isQiniuOss) {
            if (!window.CryptoJS) {
              packages.logs("CryptoJS");
              this.hide();
              return;
            }
            oss_config = this.$AVUE.qiniu;
            const token = getToken(oss_config.AK, oss_config.SK, {
              scope: oss_config.scope,
              deadline: new Date().getTime() + oss_config.deadline * 3600
            });
            param.append("token", token);
            url = oss_config.bucket
          } else if (this.isAliOss) {
            if (!window.OSS) {
              packages.logs("AliOSS");
              this.hide();
              return;
            }
            // oss_config = this.$AVUE.ali;
            // "获取当前时间2023-01-12 20:36:55与this.$AVUE.aliExpiration比较"
             if( this.$AVUE.ali.accessKeyId == "" || (this.$AVUE.aliExpiration&&new Date()>new Date(this.$AVUE.aliExpiration.replace(/\-/g, "/")))){
              // let res = this.$axios.get(this.aliConfig.stsUrl);
              // console.log('res: ', res);
              await this.$axios.get(this.aliConfig.stsUrl).then(res=>{
                let data = res.data.data;
                let  ali =  {
                  // yourRegion填写Bucket所在地域。以华东1(杭州)为例,yourRegion填写为oss-cn-hangzhou。
                  region: this.aliConfig.region,
                  // 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
                  accessKeyId: data.accessKeyId,
                  accessKeySecret: data.accessKeySecret,
                  // 从STS服务获取的安全令牌(SecurityToken)。
                  stsToken: data.securityToken,
                  // 填写Bucket名称。
                  bucket: this.aliConfig.bucket
                }
                this.$AVUE.ali = ali;
                this.$AVUE.aliExpiration = data.expiration;
              })
            }
            oss_config = this.$AVUE.ali;
            console.log('oss_config: ', oss_config);
            client = getClient(oss_config);
          }
          (() => {
            if (this.isAliOss) {
              let date = new Date();
              return client.put("/public/"+this.aliConfig.system+"/"+this.aliConfig.tenantId+"/"+date.getFullYear()+"/"+date.getMonth()+"/"+date.getDate()+"/"+uploadfile.name, uploadfile);
              // return client.put(uploadfile.name, uploadfile);
            } else {
              if (!window.axios) {
                packages.logs('axios');
                return Promise.reject()
              }
              return this.$axios.post(url, param, { headers });
            }
          })()
            .then(async res => {
              let list = {};
              if (this.isQiniuOss) {
                res.data.key = oss_config.url + res.data.key;
              }

              if (this.isAliOss) {
                let reg2 = new RegExp("/"+res.name);
                let domainUrl = res.url.replace(reg2,"");
                let params={
                  link:res.url,
                  domainUrl:domainUrl,
                  name:res.name,
                  originalName:uploadfile.name,
                  extension:(res.name).split(".")[1],
                  attachSize:uploadfile.size
                };
                await this.$axios.post(this.aliConfig.rsUrl,params).then(res1=>{
                  list = getObjValue(Object.assign({},res,{attachId:res1.data.data}), this.resKey, "object");
                });
              } else {
                list = getObjValue(res.data, this.resKey, "object");
              }

              if (typeof this.uploadAfter === "function")
                this.uploadAfter(
                  list,
                  () => {
                    this.show(list);
                  },
                  () => {
                    this.loading = false;
                  },
                  this.column
                );
              else this.show(list);
            })
            .catch(error => {
              if (typeof this.uploadAfter === "function")
                this.uploadAfter(error, this.hide, () => {
                  this.loading = false;
                }, this.column);
              else this.hide(error);
            });
        };
        if (typeof this.uploadBefore === "function")
          this.uploadBefore(this.file, callack, () => {
            this.loading = false;
          }, this.column);
        else callack();
      };
      //是否开启水印
      if (!this.validatenull(this.canvasOption)) {
        detailImg(file, this.canvasOption).then(res => {
          file = res;
          done();
        });
      } else {
        done();
      }
    },
    handleExceed (files, fileList) {
      this.$message.warning(
        `当前限制选择 ${this.limit} 个文件,本次选择了 ${
        files.length
        } 个文件,共上传了 ${files.length + fileList.length} 个文件`
      );
    },
    handlePreview (file) {
      const callback = () => {
        //判断是否为图片
        this.dialogUrl = file.url;
        if (this.typeList.img.test(file.url)) {
          this.dialogVisible = true;
          return;
        } else if (this.typeList.video.test(file.url)) {
          this.dialogVisible = true;
        }
      }
      if (typeof this.uploadPreview === "function") {
        this.uploadPreview(file, this.column, callback);
      } else {
        callback();
      }
    },
    handleDelete (file) {
      this.beforeRemove(file).then(() => {
        this.text = [];
      }).catch(() => {
      });
    },
    beforeRemove (file) {
      if (typeof this.uploadDelete === "function") {
        return this.uploadDelete(this.column, file);
      } else {
        return this.$confirm(`是否确定移除该选项?`);
      }
    }
  }
});
</script>

你可能感兴趣的:(微信小程序,flutter,uni-app)