菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
126
0

Vue+antd 上传附件a-upload组件化,附送C#异步上传文件代码【NetFrm4.5+】+Vue 增删改查代码

原创
05/13 14:22
阅读数 49987

十年河东,十年河西,莫欺少年穷

学无止境,精益求精

这是一个后端开发人员的倔强

因项目需要,我从一个纯后端变成了前端+后端,也俗称全栈。

在实际项目中,上传附件几乎是必不可少的,因此,我们有必要将上传控件进行组件化。

设计理念,通过vue的父子传值进行封装,上传组件为子组件,调用方为父组件,由父组件传值给子组件,用来校验上传的文件扩展名及大小。

子组件

<template>
  <a-upload
    :beforeUpload="beforeUpload"
    :multiple="false"
    @change="filechange"
    :customRequest="customRequest"
    :fileList="fileList"
    :remove="fileRemove"
  >
    <a-button> <a-icon type="upload" /> 选择文件 </a-button>
  </a-upload>
</template>
<script>
export default {
  props: ["fileinfo"],
  created() {},
  data() {
    return {
      fileList: [],
    };
  },
  methods: {
    beforeUpload(file) {
      var that = this;
      //console.log(file);
      return new Promise((resolve, reject) => {
        const isLt100M = file.size / 1024 / 1024 > that.fileinfo.maxsize;
        if (isLt100M) {
          this.$message.warning(
            "上传附件大小不能超过" + that.fileinfo.maxsize + "M。"
          );
          return reject(false);
        }
        var index = file.name.lastIndexOf(".");
        var suffix = file.name.substring(index + 1);
        if( that.fileinfo.filetype.indexOf(suffix)<0){
              this.$message.warning(
            "上传的文件格式不符合要求"
          );
          return reject(false);
        }
        return resolve(true);
      });
    },
    filechange(info) {
      //console.log(info);
      this.fileList = info.fileList;
      //console.log(this.fileList);

      if (info.file.status == "uploading") {
      }
      if (info.file.status === "done") {
      } else if (info.file.status === "error") {
      }
    },
    customRequest(data) {
      // 上传提交
      const formData = new FormData();
      formData.append("file", data.file);
      this.saveFile(formData);
    },
    saveFile(formData) {
      let that = this;

      this.$axios({
        url: "/api/File/UploadFileStream",
        method: "post",
        data: formData,
      })
        .then(function (result) {
          //console.log(result);
          //that.fileList.push(result.Data)
          if (result.IsSuccess) {
            for (var i = 0; i < that.fileList.length; i++) {
              that.fileList[i].status = "done";
            }
            console.log(that.fileinfo.uploadfiles);
            that.fileinfo.uploadfiles.push(result.Data);

            that.$message.success("上传附件成功。");
          } else {
            //that.fileList = [];
            for (var i = 0; i < that.fileList.length; i++) {
              if (that.fileList[i].status != "done") {
                that.fileList.splice(i, 1);
              }
            }
            that.$message.warning(result.ResultMessage);
          }
        })
        .catch(function (error) {
          console.log(error);
        });
    },
    fileRemove(file) {
      //console.log(file);
      var that = this;
      for (var i = 0; i < that.fileinfo.uploadfiles.length; i++) {
        if (
          that.fileinfo.uploadfiles[i].oldfilename == file.name &&
          that.fileinfo.uploadfiles[i].filesize == file.size
        ) {
          that.fileinfo.uploadfiles.splice(i, 1);
          //alert(1)
        }
      }
    },
  },
};
</script>
View Code

父组件

<template>
  <div>
    <a-row align="middle" class="arowLat">
          <a-col :span="12">
            <a-checkable-tag v-model="checked" @change="handleChange">
              OTA固件包
            </a-checkable-tag>
           <file-upload  v-bind:fileinfo="fileinfo"></file-upload>
          </a-col>
          <a-col :span="12"> 
             <a-button @click="submit">提交</a-button>
          </a-col>
        </a-row>
  </div>
</template>

<script>

export default {
  name: "Echarts",
  data() {
    return {
      checked:false,
      fileinfo: {
        maxsize: 1024 * 1024 * 100,
        filetype: ["jpg", "png"],
        uploadfiles: [],
      },
    };
  },
  methods: {
    submit() {
      console.log(this.fileinfo.uploadfiles);
    }
  },
  mounted() {},
};
</script>
<style scoped>
.img {
  width: 100%;
  height: auto;
}
.arow {
  text-align: left;
}

.arowLat {
  text-align: left;
  margin-top: 25px;
}
</style>
View Code

下面来简单分析下上述源码

父组件Data中fileinfo的属性用来校验文件大小,扩展名

  data() {
    return {
      checked:false,
      fileinfo: {
        maxsize: 1024 * 1024 * 100,
        filetype: ["jpg", "png"],
        uploadfiles: [],
      },
    };
  }

注:uploadfiles 用于接收子组件请求后台的结果,我的后台返回值为:

{
    Data {
        LocalFilePath: "E:\IOT\IotApi\Content\OTA\4dccdff9bc17413c96d98031a8a451ec.bin",
        filedata: [80, 19, 0, 32, 1, 33, 0, 8, 1, 139, 0, 8, 69, 114, 0, 8, 229, 138, 0, 8, 43, 74, 0, 8, 219, 182, 0, 8, 0, 0...],
        filename: "4dccdff9bc17413c96d98031a8a451ec.bin",
        filepath: "~/Content/OTA/4dccdff9bc17413c96d98031a8a451ec.bin",
        filesize: 49336,
        oldfilename: "LBS-1522-YQ4501.jpg",
    }
IsSuccess:
true, ResultCode: 0, ResultMessage: "请求成功...", }

fileinfo 用于传值给子组件,子组件中有对应的 props,如下

  props: ["fileinfo"],

a-upload 组件中有个名叫 beforeUpload 的钩子函数,我们用来校验上传的文件,如果通过校验,则上传

beforeUpload(file) {
      var that = this;
      //console.log(file);
      return new Promise((resolve, reject) => {
        const isLt100M = file.size / 1024 / 1024 > that.fileinfo.maxsize;
        if (isLt100M) {
          this.$message.warning(
            "上传附件大小不能超过" + that.fileinfo.maxsize + "M。"
          );
          return reject(false);
        }
        var index = file.name.lastIndexOf(".");
        var suffix = file.name.substring(index + 1);
        if( that.fileinfo.filetype.indexOf(suffix)<0){
              this.$message.warning(
            "上传的文件格式不符合要求"
          );
          return reject(false);
        }
        return resolve(true);
      });
    },

fileRemove 方法用来同步移除 uploadfiles 的元素,保持和组件显示的上传文件一致。

    fileRemove(file) {
      //console.log(file);
      var that = this;
      for (var i = 0; i < that.fileinfo.uploadfiles.length; i++) {
        if (
          that.fileinfo.uploadfiles[i].oldfilename == file.name &&
          that.fileinfo.uploadfiles[i].filesize == file.size
        ) {
          that.fileinfo.uploadfiles.splice(i, 1);
          //alert(1)
        }
      }
    },

在组件开发阶段,遇到一个问题,上传成功后,上传的状态一直是 uploading ,为了解决这个问题,我们需要在 filechange、customRequest、saveFile 一起解决。

主要是在 filechange 中给filelist赋值,在 saveFile 中当文件上传成功后,修改状态为done,如果上传失败,我们需要将filelist中上传失败的元素移除。

    filechange(info) {
      //console.log(info);
      this.fileList = info.fileList;
      //console.log(this.fileList);

      if (info.file.status == "uploading") {
      }
      if (info.file.status === "done") {
      } else if (info.file.status === "error") {
      }
    },
    customRequest(data) {
      // 上传提交
      const formData = new FormData();
      formData.append("file", data.file);
      this.saveFile(formData);
    },
    saveFile(formData) {
      let that = this;

      this.$axios({
        url: "/api/File/UploadFileStream",
        method: "post",
        data: formData,
      })
        .then(function (result) {
          //console.log(result);
          //that.fileList.push(result.Data)
          if (result.IsSuccess) {
            for (var i = 0; i < that.fileList.length; i++) {
              that.fileList[i].status = "done";
            }
            console.log(that.fileinfo.uploadfiles);
            that.fileinfo.uploadfiles.push(result.Data);

            that.$message.success("上传附件成功。");
          } else {
            //that.fileList = [];
            for (var i = 0; i < that.fileList.length; i++) {
              if (that.fileList[i].status != "done") {
                that.fileList.splice(i, 1);
              }
            }
            that.$message.warning(result.ResultMessage);
          }
        })
        .catch(function (error) {
          console.log(error);
        });
    },
View Code

以上便是整个上传组件。效果图有点丑

 

 在父组件,点击提交,即可得到请求上传接口的返回值

 

 这样整个组件也就做完了,有了这个组件,以后我们只需在父组件中引用子组件,并添加Props传值对象fileinfo即可,从而节省了大量的重复代码。

下面是C#的后端,不做解释,只贴代码。详细可参考:https://www.cnblogs.com/chenwolong/p/Uplode.html
using Iot.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Http.Cors;

namespace Iot.WebSite.Controllers.Apis
{
    [RoutePrefix("api/file")]
    [EnableCors(origins: "*", headers: "*", methods: "GET,POST,PUT,DELETE")]
    public class FileController : ApiController
    {
        /// <summary>
        /// 上传文件
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public async Task<BaseResponse> UploadFileStream()
        {
            var returns = CommonBaseResponse.SetResponse<fileInfoModel>(null, false);
            string fileType = "OTA";//要创建的子文件夹的名字
            var uploadPath = "~/Content";
            string filePath = System.Web.HttpContext.Current.Server.MapPath(uploadPath + "/" + fileType + "/");//绝对路径
            //string filePath = uploadPath + "\\" + fileType + "\\";  //E:\Fileup  居家
            if (Directory.Exists(filePath) == false)
            {
                Directory.CreateDirectory(filePath);
            }
            try
            {
                var provider = new ReNameMultipartFormDataStreamProvider(filePath);
                await Request.Content.ReadAsMultipartAsync(provider).ContinueWith(o =>
                {

                    foreach (var file in provider.FileData)
                    {
                        string orfilename = file.Headers.ContentDisposition.FileName.TrimStart('"').TrimEnd('"');//待上传的文件名
                        FileInfo fileinfo = new FileInfo(file.LocalFileName);
                        //判断开始
                        string oldName = orfilename;//选择的文件的名称
                        string fileExt = orfilename.Substring(orfilename.LastIndexOf('.'));
                        string Extension = fileExt;
                        string CreateTime = DateTime.Now.ToString("yyyyMMddHHmmss");

                        fileInfoModel fileResult = new fileInfoModel()
                        {
                            oldfilename = oldName,
                            LocalFilePath = file.LocalFileName,
                            filesize = fileinfo.Length,
                            filename = fileinfo.Name,
                            filepath = uploadPath + "/" + fileType + "/" + fileinfo.Name
                        };

                        var fs = fileinfo.OpenRead();
                        byte[] buffur = new byte[fs.Length];
                        fs.Read(buffur, 0, (int)fs.Length);
                        fileResult.filedata = buffur.ToList();
                        returns = CommonBaseResponse.SetResponse<fileInfoModel>(fileResult, true);
                    }
                });
            }
            catch (Exception ex)
            {
                LogHelper.WriteLog("上传附件出错:" + ex.ToString());
            }
            return returns;
        }
    }
    /// <summary>
    /// 重命名上传的文件
    /// </summary>
    public class ReNameMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
    {
        public ReNameMultipartFormDataStreamProvider(string root)
            : base(root)
        { }

        public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
        {

            string extension = !string.IsNullOrWhiteSpace(headers.ContentDisposition.FileName) ? Path.GetExtension(GetValidFileName(headers.ContentDisposition.FileName)) : "";
            return Guid.NewGuid().ToString().Replace("-", "") + extension;
        }

        private string GetValidFileName(string filePath)
        {
            char[] invalids = System.IO.Path.GetInvalidFileNameChars();
            return String.Join("_", filePath.Split(invalids, StringSplitOptions.RemoveEmptyEntries)).TrimEnd('.');
        }

    }

    public class fileInfoModel
    {
        /// <summary>
        /// 新文件名称
        /// </summary>
        public string filename { get; set; }
        /// <summary>
        /// 老文件名称
        /// </summary>
        public string oldfilename { get; set; }
        /// <summary>
        /// 服务器绝对地址
        /// </summary>
        public string LocalFilePath { get; set; }
        /// <summary>
        /// 文件大小 字节
        /// </summary>
        public long filesize { get; set; }
        /// <summary>
        /// 问价数据
        /// </summary>
        public List<byte> filedata { get; set; }
        /// <summary>
        /// 文件相对路径
        /// </summary>
        public string filepath { get; set; }
    }
}
View Code

增删改查代码,只做记录,方便自己以后查看

 

<template>
  <div>
    <a-card
      hoverable="true"
      title=""
      headStyle="text-align:left;color:#606266;font-size:14px"
      bodyStyle="border:none"
    >
      <a slot="extra" href="#" style="float: left">
        <a-breadcrumb separator=">">
          <a-breadcrumb-item>OTA升级</a-breadcrumb-item>

          <a-breadcrumb-item>电池OTA升级 </a-breadcrumb-item>
        </a-breadcrumb>
      </a>
      <div>
        <a-row align="middle" class="arow">
          <a-col :span="6">
            <a-checkable-tag v-model="checked" @change="handleChange">
              OTA版本号
            </a-checkable-tag>
            <a-input
              placeholder="请输入OTA版本号"
              style="width: 180px"
              v-model="SearchInfo.SoftWare"
            />
          </a-col>
          <a-col :span="6">
            <a-checkable-tag v-model="checked" @change="handleChange">
              BMS版本号
            </a-checkable-tag>
            <a-input
              placeholder="请输入BMS版本号"
              style="width: 180px"
              v-model="SearchInfo.OldSoftWare"
            />
          </a-col>
          <a-col :span="6"> </a-col>
          <a-col :span="6">
            <a-button type="primary" @click="Search"> 查询 </a-button>
            &nbsp;
            <a-button type="danger" @click="() => setModal1Visible(true)">
              新增
            </a-button>
          </a-col>
        </a-row>

        <a-table
          :columns="columns"
          :data-source="data"
          style="margin-top: 20px"
        >
          <template slot="action" slot-scope="text, record">
            <a slot="action" href="javascript:;" @click="deleterow(record)"
              >删除</a
            >
            <a slot="action"  href="javascript:;" @click="onEdit(record)" >编辑</a> 
            <!-- <span slot="action" slot-scope="text" href="javascript:;" @click="onEdit()" >删除</span> -->
          </template>
        </a-table>
      </div>
    </a-card>

    <a-modal
      title="新增OTA升级信息"
      :width="760"
      :okText="提交"
      :cancelText="取消"
      :dialog-style="{ top: '20px' }"
      :visible="modal1Visible"
      @ok="() => Save()"
      @cancel="() => setModal1Visible(false)"
    >
      <a-card
        hoverable="true"
        title=""
        headStyle="text-align:left;color:#606266;font-size:14px"
        bodyStyle="border:none"
      >
        <a-row align="middle" class="arow">
          <a-col :span="12">
            <a-checkable-tag v-model="checked" @change="handleChange">
              OTA版本号
            </a-checkable-tag>
            <a-input
              placeholder="请输入OTA版本号"
              style="width: 180px"
              v-model="OtaModel.SoftWare"
            />
          </a-col>
          <a-col :span="12">
            <a-checkable-tag v-model="checked" @change="handleChange">
              单包大小
            </a-checkable-tag>
            <a-input-number
              v-model="OtaModel.SigPackSize"
              placeholder="请输入单包大小"
              :min="64"
              :max="2048"
              style="width: 180px"
            />
          </a-col>
        </a-row>
        <a-row align="middle" class="arowLat">
          <a-col :span="12">
            <a-checkable-tag v-model="checked" @change="handleChange">
              BMS版本号
            </a-checkable-tag>
            <a-input
              placeholder="请输入BMS版本号"
              style="width: 180px"
              v-model="OtaModel.OldSoftWare"
            />
          </a-col>
          <a-col :span="12">
            <a-checkable-tag v-model="checked" @change="handleChange">
              升级描述
            </a-checkable-tag>
            <a-input
              placeholder="请输入升级描述"
              style="width: 180px"
              v-model="OtaModel.OtaFileInfo"
            />
          </a-col>
        </a-row>
        <a-row align="middle" class="arowLat">
          <a-col :span="12">
            <a-checkable-tag v-model="checked" @change="handleChange">
              OTA固件包
            </a-checkable-tag>
            <file-upload v-bind:fileinfo="fileinfo"></file-upload>
          </a-col>
          <a-col :span="12"> </a-col>
        </a-row>
      </a-card>
    </a-modal>
  </div>
</template>

<script>
import moment from "moment";
import "moment/locale/zh-cn";
import locale from "ant-design-vue/es/date-picker/locale/zh_CN";
const formatterTime = (val) => {
  alert(val);
  return val ? moment(val).format("YYYY-MM-DD HH:mm:ss") : "";
};
const columns = [
  {
    title: "升级描述",
    width: 200,
    dataIndex: "OtaFileInfo",
    key: "OtaFileInfo",
    align: "center",
  },
  { title: "固件大小(字节)", dataIndex: "FileSize", key: "1", align: "center" },
  { title: "BMS版本号", dataIndex: "OldSoftWare", key: "2", align: "center" },
  { title: "OTA版本号", dataIndex: "SoftWare", key: "3", align: "center" },
  {
    title: "单包大小(字节)",
    dataIndex: "SigPackSize",
    key: "4",
    align: "center",
  },
  {
    title: "创建日期",
    dataIndex: "CreateTime",
    key: "8",
    align: "center",
    customRender: function (val) {
      return val ? moment(val).format("YYYY-MM-DD") : "";
    },
  },
  {
    title: "操作",
    key: "operation",
    align: "center",
    width: 100,
    scopedSlots: { customRender: "action"},
  },
];

const data = [];

export default {
  name: "Echarts",
  data() {
    return {
      locale,
      checked: false,
      checkedmodal: true,
      dateFormat: "YYYY/MM/DD",
      monthFormat: "YYYY/MM",
      data,
      columns,
      modal1Visible: false,
      fileinfo: {
        maxsize: 1024 * 1024 * 10,
        filetype: ["bin"],
        uploadfiles: [],
      },
      OtaModel: {
        OldSoftWare: "",
        SoftWare: "",
        SigPackSize: 512,
        OtaFileInfo: "",
        FileName: "",
        oldfilename: "",
        LocalFilePath: "",
        filesize: 0,
        filedata: [],
        FilePath: "",
      },
      SearchInfo: {
        SoftWare: "",
        OldSoftWare: "",
        PageNumber: 1,
        PageSize: 1,
        Stime: "2020-01-01",
        Etime: "2030-01-01",
      },
    };
  },
  methods: {
    moment,
    onChange(dates, dateStrings) {
      console.log("From: ", dates[0], ", to: ", dates[1]);
      console.log("From: ", dateStrings[0], ", to: ", dateStrings[1]);
    },
    handleChange(checked) {},
    setModal1Visible(modal1Visible) {
      this.modal1Visible = modal1Visible;
    },
    Save() {
      let that = this;
      if (!that.OtaModel.OldSoftWare) {
        that.$message.error("请输入BMS版本号。");
        return;
      } else if (!that.OtaModel.SoftWare) {
        that.$message.error("请输入OTA版本号。");
        return;
      } else if (!that.OtaModel.SigPackSize) {
        that.$message.error("请输入单包大小。");
        return;
      } else if (!that.OtaModel.OtaFileInfo) {
        that.$message.error("请输入OTA版本描述");
        return;
      } else if (
        !that.fileinfo.uploadfiles ||
        that.fileinfo.uploadfiles.length != 1
      ) {
        that.$message.error("请上传升级固件包【有且仅能上传一个固件包】");
        return;
      } else {
        that.OtaModel.FileName = that.fileinfo.uploadfiles[0].filename;
        that.OtaModel.oldfilename = that.fileinfo.uploadfiles[0].oldfilename;
        that.OtaModel.LocalFilePath =
          that.fileinfo.uploadfiles[0].LocalFilePath;
        that.OtaModel.filesize = that.fileinfo.uploadfiles[0].filesize;
        that.OtaModel.filedata = that.fileinfo.uploadfiles[0].filedata;
        that.OtaModel.FilePath = that.fileinfo.uploadfiles[0].filepath;
        //console.log(that.OtaModel);

        that
          .$axios({
            url: "/api/BatteryOta/CreateBatteryOtaSoftWareInfo",
            method: "post",
            data: that.OtaModel,
          })
          .then(function (result) {
            if (result.IsSuccess) {
              that.OtaModel = {
                OldSoftWare: "",
                SoftWare: "",
                SigPackSize: 512,
                OtaFileInfo: "",
                FileName: "",
                oldfilename: "",
                LocalFilePath: "",
                filesize: 0,
                filedata: [],
                FilePath: "",
              };
              that.modal1Visible = false;
            } else {
              that.$message.error(result.ResultMessage);
              that.OtaModel = {
                OldSoftWare: "",
                SoftWare: "",
                SigPackSize: 512,
                OtaFileInfo: "",
                FileName: "",
                oldfilename: "",
                LocalFilePath: "",
                filesize: 0,
                filedata: [],
                FilePath: "",
              };
            }
          })
          .catch(function (error) {
            console.log(error);
          });
      }
    },
    Search() {
      let that = this;
      that
        .$axios({
          url: "/api/BatteryOta/SearchOtaSoftWareInfo",
          method: "post",
          data: that.SearchInfo,
        })
        .then(function (result) {
          console.log(result);
          if (result.IsSuccess) {
            that.data = result.Data.Data;
          } else {
            that.$message.error(result.ResultMessage);
          }
        })
        .catch(function (error) {
          console.log(error);
        });
    },
    deleterow(record) {
      console.log(record);
    },
  },
  mounted() {},
  created() {
    this.Search();
  },
};
</script>
<style scoped>
.img {
  width: 100%;
  height: auto;
}
.arow {
  text-align: left;
}

.arowLat {
  text-align: left;
  margin-top: 25px;
}
</style>
View Code

 

@天才卧龙的博客

发表评论

0/200
126 点赞
0 评论
收藏