> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# OSS 上传「FormData」

## 需求简介：

开发过程中，多多少少会用到图片上传、文件上传等，大多数都会选择第三方云服务提供的相关功能，比如阿里云、腾讯云、七牛云等等云服务提供商提供的文件存储运营商提供的服务。本次介绍使用的主要是阿里云的 **OSS** 文件直传，阿里云存储文件方式有多种，目前主要就工作中用到的阿里云 **oss** 文件直传做案例做笔记，以便于帮助有类似需求的同行及自己学习。

## 注意事项：

1. **文件分类**：主要是方便日后文件的管理，同时也便于查找。
2. **文件重命名**：上传文件有时候会出现重名等问题，有时候会给使用者造成一定的影响，不利用用户区分文件是否上传；统一规则对文件进行命名，目的是为了方便日后文件管理维护等。
3. **文件大小限制**：限制上传文件的大小，主要是减少上传完文件等待的时间，以及带宽的消耗，同时也可以减少云存储的存储空间的占用。
4. **文件存储归类**：依据文件的类型、格式、时间等进行文件的归类，目的在于文件规划合理，存储有序，便于后期的管理和维护。

## 代码实例：

#### OSS 配置

```javascript title="OSS 配置参数"
/**
 * [OSS配置]
 * @type {Object}
 */
const conf = {
    accessKeyId: "accessKeyId", // OSS上传需要的Id
    accessKeySecret: "accessKeySecret", // OSS上传需要的Secret
    ali: "https://xxxxx.oss-cn-beijing.aliyuncs.com", // 阿里云OSS存储对象地址
    bucket: "xxxxx", // 类型
};
```

### 文件重命名（文件分类）

```javascript title="文件重命名"
/**
 * [文件重命名]
 * @param  {String} suffixName [文件后缀名]
 * @return {String}            [文件相对路径名]
 */
const fileRename = (suffixName) => {
    // 处理时间戳
    const moment = new Date();
    let yyyy = moment.getFullYear(),
        MM = moment.getMonth() + 1,
        dd = moment.getDate(),
        hh = moment.getHours(),
        mm = moment.getMinutes(),
        ss = moment.getSeconds();
    // 补“0”
    MM = MM <= 9 ? "0" + MM : MM;
    dd = dd <= 9 ? "0" + dd : dd;
    // 5位数字的随机数
    let rm = (Math.random() * 1e5).toFixed(),
        path = `${yyyy}${MM}/${dd}_${hh}${mm}${ss}_${rm}`;
    return path + suffixName;
};
// 格式：yyyyMM/dd_hhmmss_rm.fileType
```

#### 上传图片函数（uploadImg）

```javascript title="上传图片"
/**
 * [上传图片]
 * @param  {Object} file     [上传文件实例]
 * @param  {String} filePath [上传文件相对路径]
 * @return {String}          [上传图片生成的路径地址]
 */
const uploadImg = async (file, filePath) => {
    let path = filePath.toLowerCase(),
        dotIndex = path.lastIndexOf("."),
        fileType = path.substring(dotIndex);
    const typeString = ".jpg.jpeg.png";
    // 限制类型
    if (!typeString.includes(fileType)) {
        Message.error("上传图片类型有误，请重新上传！");
        return null;
    }
    // 限制大小
    if (file.size >= 1024 * 1024) {
        this.$message.error("上传的图片大小不能超过 1M，请重新上传！");
        return null;
    }
    // 文件后缀名
    let suffixName = fileRename(fileType);
    // FormData
    const form = new FormData();
    form.append("key", "picture/" + suffixName);
    form.append("file", file);
    form.append("accessKeyId", conf.accessKeyId);
    form.append("success_action_status", 200);

    try {
        const res = await axios.post(conf.ali, form);
        if (res.status == 200) {
            Message.success("上传成功");
            return conf.ali + "/picture/" + suffixName;
        } else {
            Message.error("上传失败：" + res.status);
            return null;
        }
    } catch (error) {
        console.log(error);
        Message.error("图片上传失败");
        return null;
    }
};
```

#### 实例绑定 vue

```javascript title="文件上传 - 示例绑定"
import Vue from "vue";
import App from "./App.vue";
// 文件上传
import uploadImg from "./assets/js/uploadImg";

Object.defineProperties(Vue.prototype, {
    $oss: { value: uploadImg },
});

new Vue({
    render: (h) => h(App),
}).$mount("#app");
```

## 实例使用：

```html title="实例使用"
<template>
    <el-form
        :model="user"
        ref="userForm"
        :rules="rules"
        size="small"
        label-width="80px"
        class="dialog-form"
    >
        <el-form-item label="上传图片">
            <el-row>
                <el-col :span="8">
                    <el-button
                        type="primary"
                        icon="el-icon-upload"
                        class="uploadBtn"
                        :loading="loading"
                    >
                        上传图片
                        <input
                            type="file"
                            accept=".jpg, .jpeg, .png"
                            @change="uploadImg"
                        />
                    </el-button>
                    <p class="up-tip">
                        注：若输入用户头像链接地址同时又上传图片，则上传的图片优先级高于输入的链接地址（建议尺寸200*200）
                    </p>
                </el-col>
                <!-- 展示图片 -->
                <el-col :span="15" :offset="1">
                    <el-image
                        :src="avatar"
                        draggable="false"
                        style="width:144px;height:144px;"
                    >
                        <div slot="error" class="image-slot">
                            <i class="el-icon-picture-outline"></i>
                        </div>
                    </el-image>
                </el-col>
            </el-row>
        </el-form-item>
    </el-form>
</template>

<script>
    export default {
        name: "UploadImg",
        data() {
            return {
                loading: false,
                avatar: "",
            };
        },
        methods: {
            // 上传图片
            uploadImg(e) {
                // 加载指示器
                this.loading = true;
                // file实例
                let file = e.target.files[0];
                // file相对路径
                let filePath = e.target.value;
                // 文件上传
                this.$oss(file, filePath).then((url) => {
                    this.loading = false;
                    !!url && (this.avatar = url);
                });
            },
        },
    };
</script>
```

:::danger 注意事项
图片上传主要 **_按钮_** `<input type="file" accept=".jpg, .jpeg, .png" @change="uploadImg" />` ，其中 **_accept_** 作用主要是过滤指定类型文件，默认为空「**""**」（即不做任何限制）。
:::
