Vue + WebApi 实现上传下载功能(完整示例)

 更新时间:2025年10月22日 15:41:41   作者:_tiddler  
本文通过实例代码介绍Vue + WebApi实现上传下载功能,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧

vue上传:

UI:

  <el-button
        size="mini"
        type="text"
        icon="el-icon-document-add"
        @click="handleUpload()"
        >上传
      </el-button>
      <el-dialog
        :title="Title"
        :visible.sync="open"
        width="600px"
        append-to-body
        :close-on-click-modal="false"
      >
        <el-upload
          ref="upload"
          :limit="1"
          :on-remove="handleRemove"
          :on-error="onError"
          :file-list="fileList"
          :auto-upload="false"
          :http-request="customUpload"
          action="http://localhost:5000/api/Resource/AddResource"
          class="upload-demo"
        >
          <el-button slot="trigger" size="small" type="primary"
            >选取文件</el-button
          >
          <el-button
            style="margin-left: 10px;"
            size="small"
            type="success"
            @click="submitUpload"
            >上传到服务器</el-button
          >
          <div slot="tip" class="el-upload__tip">
            支持上传 {{ strRebuild(fileType) }} 格式,且不超过 {{ fileSize }}M
          </div>
        </el-upload>
      </el-dialog>

点击按钮触发方法,open为true 上传弹窗出现:

handleUpload() {
      this.title = "XXXX";
      this.open = true;
    },

选择文件后,点击上传到服务器,触发以下方法:

 customUpload(file) {
      console.debug("进入上传方法");
      const param = new FormData();
      param.append("files", file.file);
      console.log("上传文件:", file);
      console.log("FormData:", param);
      uploadFile(param);
      setTimeout(() => {
        this.open = false;
        message("success", "上传成功");
        this.$refs.upload.clearFiles();
        this.fileList = [];
      }, 1500);
    },

其中触发封装的请求方法:

export async function uploadFile(file) {
  try {
    console.log("上传资源参数:", file);
    // 尝试不同的路径格式
    
    const response = await request.post("/Resource/UploadFile", file);

    console.log("API响应:", response);
    return response;
  } catch (error) {
    console.error("获取文章列表失败:", error);
    throw error;
  }
}

.Net 后端Api:

/// <summary>
/// 上传客户端文件并保存
/// </summary>
[HttpPost("UploadFile")]
public async Task<IActionResult> UploadFile(IFormFileCollection files)
{
    foreach (var file in files)
    {
        // 判断文件是否有内容
        if (file.Length == 0)
        {
            Console.WriteLine("该文件无任何内容!!!");
            continue;
        }
        // 获取附件原名
        string fileName = file.FileName;
        // 如果是获取的含有路径的文件名,那么截取掉多余的,只剩下文件名和后缀名
        if (fileName.Contains("\\"))
        {
            int index = fileName.LastIndexOf("\\");
            fileName = fileName.Substring(index + 1);
        }
        // 判断单个文件大于1M
        long fileSize = file.Length;
        if (fileSize > 1024 * 1024)
        {
            Console.WriteLine($"文件大小为(单位字节):{fileSize}");
            Console.WriteLine("该文件大于1M");
        }
        // 构建完整保存路径
        var fullPath = Path.Combine(_webHostEnvironment.WebRootPath, $"Resources/{Guid.NewGuid()}{fileName}");
        var directory = Path.Combine(_webHostEnvironment.WebRootPath, $"Resources");
        if (!Directory.Exists(directory))
        {
            Directory.CreateDirectory(directory);
        }
        try
        {
            // 将文件保存到指定位置
            using (var stream = new FileStream(fullPath, FileMode.Create))
            {
                await file.CopyToAsync(stream);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
            return StatusCode(500, new { message = "文件上传失败", error = e.Message });
        }
    }
    return Ok(new { success = true, message = "上传成功" });
}

可以使用IFormFileCollection 接口类型对接前端的FormData类型。通过FileStream 类传输文件。

因为_webHostEnvironment.WebRootPath对应wwwroot,所以文件会保存在wwwroot目录下。

其实应该有对应的表格。上传的文件信息会保存在数据库里,这里图方便就没写。

下载:

下载的方法有很多种,这里展示Fetch下载:

 downloadFile(fileName) {
      const fileUrl = this.$baseURL + "\\Resources\\" + fileName; // 文件的URL地址
      fetch(fileUrl)
        .then((response) => response.blob())
        .then((blob) => {
          const url = window.URL.createObjectURL(blob);
          const link = document.createElement("a");
          link.href = url;
          link.setAttribute("download", fileName);
          document.body.appendChild(link);
          link.click();
        })
        .catch((error) => {
          console.error(error);
        });
    },

按钮调用:

<button @click="downloadFile('test.png')">下载文件2</button>

在方法里直接拼接了图片(其他文件类似)的url,会在浏览器中下载以下图片:

完整vue代码:

<template>
  <div class="front-container">
    <div>
      <h2>往昔峥嵘</h2>
      <el-button
        size="mini"
        type="text"
        icon="el-icon-document-add"
        @click="handleUpload()"
        >上传
      </el-button>
      <el-dialog
        :title="Title"
        :visible.sync="open"
        width="600px"
        append-to-body
        :close-on-click-modal="false"
      >
        <el-upload
          ref="upload"
          :limit="1"
          :on-remove="handleRemove"
          :on-error="onError"
          :file-list="fileList"
          :auto-upload="false"
          :http-request="customUpload"
          action="http://localhost:5000/api/Resource/AddResource"
          class="upload-demo"
        >
          <el-button slot="trigger" size="small" type="primary"
            >选取文件</el-button
          >
          <el-button
            style="margin-left: 10px;"
            size="small"
            type="success"
            @click="submitUpload"
            >上传到服务器</el-button
          >
          <div slot="tip" class="el-upload__tip">
            支持上传 {{ strRebuild(fileType) }} 格式,且不超过 {{ fileSize }}M
          </div>
        </el-upload>
      </el-dialog>
      <button @click="downloadFile('test.png')">下载文件2</button>
    </div>
    <div class="blog-list">
      <BlogCard
        v-for="blog in blogs"
        :key="blog.id"
        :blog="blog"
        @view-detail="handleViewDetail"
      />
    </div>
  </div>
</template>
<script>
import { lastSubstring, strRebuildEx } from "@/utils/util";
import { message } from "@/utils/message";
import { blogList } from "../mock/blogData.ts";
import { uploadFile } from "@/api/resources";
import BlogCard from "@/components/BlogCard.vue";
export default {
  name: "BlogList",
  components: {
    BlogCard,
  },
  data() {
    return {
      Title: "上传组件",
      open: false,
      blogs: blogList,
      // 附件列表
      fileList: [],
      // 允许的文件类型,可依据实际需求增加格式
      fileType: [
        "xls",
        "xlsx",
        "pdf",
        "doc",
        "docx",
        "txt",
        "jpg",
        "png",
        "jpeg",
        "zip",
      ],
      //fileType: ["pdf", "doc", "zip"],
      // 运行上传文件大小,单位 M
      fileSize: 10,
    };
  },
  methods: {
    handleViewDetail(blog) {
      console.log("查看博客详情:", blog);
    },
    // downloadFile(fileName) {
    //   const fileUrl = this.$baseURL + "\\Resources\\" + fileName; // 文件的URL地址
    //   console.debug(fileUrl);
    //   const link = document.createElement("a");
    //   link.href = fileUrl;
    //   link.setAttribute("download", fileName);
    //   link.click();
    // },
    downloadFile(fileName) {
      const fileUrl = this.$baseURL + "\\Resources\\" + fileName; // 文件的URL地址
      fetch(fileUrl)
        .then((response) => response.blob())
        .then((blob) => {
          const url = window.URL.createObjectURL(blob);
          const link = document.createElement("a");
          link.href = url;
          link.setAttribute("download", fileName);
          document.body.appendChild(link);
          link.click();
        })
        .catch((error) => {
          console.error(error);
        });
    },
    handleUpload() {
      this.title = "XXXX";
      this.open = true;
    },
    // 清空表单
    clear() {
      // 清空附件
      this.$refs.upload.clearFiles();
    },
    // 附件检查
    // 检查附件是否属于可上传类型
    // 检查附件是否超过限制大小
    checkFile() {
      var flag = true;
      var tip = "";
      var files = this.$refs.upload.uploadFiles;
      files.forEach((item) => {
        // 文件过大
        if (item.size > this.fileSize * 1024 * 1024) {
          flag = false;
          tip = " 文件超过" + this.fileSize + "M";
        }
        // 文件类型不属于可上传的类型
        if (!this.fileType.includes(lastSubstring(item.name, "."))) {
          flag = false;
          tip = " 文件类型不可上传";
          this.clientOpen = false;
        }
      });
      if (!flag) {
        message("error", tip);
      }
      return flag;
    },
    // 提交附件
    submitUpload() {
      if (this.checkFile()) {
        console.log("上传附件...");
        this.$refs.upload.submit();
      } else {
        console.log("取消上传");
      }
    },
    // 自定义文件上传方法
    customUpload(file) {
      console.debug("进入上传方法");
      const param = new FormData();
      param.append("files", file.file);
      // 模拟上传成功
      console.log("上传文件:", file);
      console.log("FormData:", param);
      uploadFile(param);
      // 模拟API调用
      setTimeout(() => {
        this.open = false;
        message("success", "上传成功");
        this.$refs.upload.clearFiles();
        this.fileList = [];
      }, 1500);
    },
    // 移除附件
    handleRemove(file, fileList) {
      console.log("移除附件...");
    },
    // 附件上传失败,打印下失败原因
    onError(err) {
      message("error", "附件上传失败");
      console.log(err);
    },
    strRebuild(arr) {
      strRebuildEx(arr, ",");
    },
  },
};
</script>
<style>
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: #2c3e50;
  padding: 1rem 2rem;
}
.nav-brand {
  color: white;
  font-size: 1.2rem;
  font-weight: bold;
}
.nav-links {
  display: flex;
  align-items: center;
  gap: 0;
}
.nav-link {
  color: white;
  text-decoration: none;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  transition: all 0.3s ease;
  white-space: nowrap;
}
.nav-link:hover {
  background-color: #34495e;
  transform: translateY(-1px);
}
.router-link-active {
  background-color: #42b883;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.separator {
  width: 1px;
  height: 20px;
  background-color: rgba(255, 255, 255, 0.3);
  margin: 0 0.5rem;
}
.el-row {
  margin-bottom: 20px;
}
.el-col {
  border-radius: 4px;
}
.bg-purple-dark {
  background: #99a9bf;
}
.bg-purple {
  background: #d3dce6;
}
.bg-purple-light {
  background: #e5e9f2;
}
.grid-content {
  border-radius: 4px;
  min-height: 36px;
}
.row-bg {
  padding: 10px 0;
  background-color: #f9fafc;
}
.viewmore-row {
  float: right;
  background: #12b7de;
  color: #fff;
  border-radius: 3px;
  padding: 0px 10px;
  height: 30px;
}
.bg-purple {
  background: #eaeaea;
  -webkit-animation: loading 1s ease-in-out infinite;
  animation: loading 1s ease-in-out infinite;
}
@keyframes loading {
  0% {
    width: 90%;
  }
  50% {
    width: 100%;
  }
  to {
    width: 90%;
  }
}
[v-cloak] {
  display: none !important;
}
</style>

根据自己需要把其中报错,不相干的代码删减掉。

到此这篇关于Vue + WebApi 实现上传下载功能的文章就介绍到这了,更多相关Vue WebApi上传下载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue中各组件之间传递数据的方法示例

    vue中各组件之间传递数据的方法示例

    Vue 的组件作用域都是孤立的,不允许在子组件的模板内直接引用父组件的数据。必须使用特定的方法才能实现组件之间的数据传递。下面这篇文章主要给大家介绍了关于vue中各组件之间传递数据的方法示例,需要的朋友可以参考学习。
    2017-07-07
  • Vue动态添加表单的实现方法

    Vue动态添加表单的实现方法

    在Vue.js应用中,动态表单是一个常见的需求,尤其是当表单字段的数量和类型需要根据用户输入或系统状态动态变化时,本文将详细介绍如何在Vue中实现动态表单的创建,并通过多个示例展示具体的实现方法,需要的朋友可以参考下
    2024-09-09
  • vue3中使用jsx的实现步骤

    vue3中使用jsx的实现步骤

    本文主要介绍了vue3中使用jsx的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Vue.js tab实现选项卡切换

    Vue.js tab实现选项卡切换

    这篇文章主要为大家详细介绍了Vue.js组件tab实现选项卡切换效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • vue-cli2.x旧版本卸载不掉的问题踩坑指南(附Vue脚手架安装教程)

    vue-cli2.x旧版本卸载不掉的问题踩坑指南(附Vue脚手架安装教程)

    遇到一个Vuecli2脚手架卸载不了的问题,查了许多资料说的都比较复杂,所以下面这篇文章主要给大家介绍了关于vue-cli2.x旧版本卸载不掉的问题踩坑的相关资料,文中还附了Vue脚手架安装教程,需要的朋友可以参考下
    2022-07-07
  • vue结合leaflet实现热力图

    vue结合leaflet实现热力图

    本文主要介绍了vue实现热力图,结合leaflet.heat插件可以很容易的做出热力图,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • vue+element+Java实现批量删除功能

    vue+element+Java实现批量删除功能

    这篇文章主要介绍了vue+element+Java实现批量删除功能,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-04-04
  • vue3卡片垂直无限滚动方式

    vue3卡片垂直无限滚动方式

    使用CSS动画实现DOM向上滚动,JS动态计算数据条数并实时调整滚动速度,确保在onMounted中调用以正确渲染DOM
    2025-07-07
  • Vue3+TypeScript实现PDF预览组件

    Vue3+TypeScript实现PDF预览组件

    这篇文章主要为大家详细介绍了如何基于Vue3+TypeScript实现PDF预览组件,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-04-04
  • vue2.0结合Element实现select动态控制input禁用实例

    vue2.0结合Element实现select动态控制input禁用实例

    本篇文章主要介绍了vue2.0结合Element实现select动态控制input禁用实例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05

最新评论