14---实现文件上传和下载(头像上传功能)

news2024/9/21 12:24:03

1、建Files表

  1. 接下来开始完成文件管理的内容,首先是数据库建Files表
DROP TABLE IF EXISTS `file`;

CREATE TABLE `file` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(255) DEFAULT NULL COMMENT '文件名称',
  `type` varchar(255) DEFAULT NULL COMMENT '文件类型',
  `size` bigint(20) DEFAULT NULL COMMENT '文件大小(kb)',
  `url` varchar(255) DEFAULT NULL COMMENT '下载链接',
  `is_delete` tinyint(1) DEFAULT '0' COMMENT '是否删除',
  `enable` tinyint(1) DEFAULT '1' COMMENT '是否禁用链接',
  `md5` varchar(255) DEFAULT NULL COMMENT '文件md5',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
  1. 在springboot中写实体类Files.java
@Data
@TableName("file")
public class Files {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String name;
    private String type;
    private Long size;
    private String url;
    private Boolean isDelete;
    private Boolean enable;
    private String md5;

}

  1. FilesMapper.java


@Repository
public interface FileMapper extends BaseMapper<Files> {
}

  1. FilesService.java
@Service
public class FilesService extends ServiceImpl<FilesMapper, Files> {
  
}

2、springboot实现文件上传

  1. 在配置文件中配置上传文件存入磁盘位置
files:
  upload:
    path: D:/IDEA/后台管理系统/files/

  1. FilesController.java
@RestController
@RequestMapping("/file")
public class FilesController {
    @Autowired
    private FilesService fileService;

    @Value("${files.upload.path}")
    private String fileUploadPath;

    @PostMapping("/upload")
    public String upload(@RequestParam MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String type = FileUtil.extName(originalFilename);
        long size = file.getSize();

        //定义一个文件唯一的标识码
        String uuid = IdUtil.fastSimpleUUID();
        String fileUuid = uuid + StrUtil.DOT + type;
        File uploadFile = new File(fileUploadPath + fileUuid);
        //判断配置的文件目录是否存在,若不存在则创建一个新的文件目录
        File parentFile = uploadFile.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }
        String url;
       //上传文件到磁盘
        file.transferTo(uploadFile);

        //获取文件的md5
        String md5 = SecureUtil.md5(uploadFile);
        //从数据库查询是否存在相同的记录
        Files dbFiles = getFileMd5(md5);
        if (dbFiles!=null){
            url=dbFiles.getUrl();
            //由于文件已经存在,所以删除刚才上传的重复文件
            uploadFile.delete();
        }else {
            //数据库不存在重复文件,则不删除刚才上传的文件
            url="http://localhost:8081/file/"+fileUuid;
        }
        //存储数据库
        Files saveFile = new Files();
        saveFile.setName(originalFilename);
        saveFile.setType(type);
        saveFile.setSize(size/1024);  //从b转化为kb
        saveFile.setUrl(url);
        saveFile.setMd5(md5);
        fileService.save(saveFile);
        return url;



    }
}
  • 打开swagger测试 --http://localhost:8081/swagger-ui.html

bug:打开swagger页面是,如果出现弹窗,无法打开swagger,可能是因为页面被拦截器拦截了,去拦截器中设置一下放行swagger就行:

InterceptorConfig.java

 .excludePathPatterns("/user/login","/user/register","/**/export","/**/import","/file/**","/swagger-resources/**"
                       ,"/webjars/**"
                       ,"/v2/**"
                       ,"/swagger-ui.html/**");  //拦截除登录注册、导入导出以外请求,通过判断token是否合法来决定是否需要登录  ,放行swagger
    }
  • 测试

在这里插入图片描述

点击测试后,返回一串链接,即fileUuid,下面就写文件下载接口,来下载文件

在这里插入图片描述

3、springboot实现文件下载

 //文件下载接口  就是前面上传后返回的url:http://localhost:8081/file/{fileUuid}
    @GetMapping("/{fileUuid}")
    public void download(@PathVariable String fileUuid, HttpServletResponse response) throws IOException {
       //根据文件的唯一标识码获取文件
        File uploadFile = new File(fileUploadPath + fileUuid);
        //设置输出流的格式
        ServletOutputStream os = response.getOutputStream();
        response.addHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileUuid,"UTF-8"));
        response.setContentType("application/octet-stream");

        //读取文件的字节流
        os.write(FileUtil.readBytes(uploadFile));
        os.flush();
        os.close();


    }

    //通过文件的md5查询文件
    private Files getFileMd5(String md5){
        //查询文件的md5是否存在
        QueryWrapper<Files>queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("md5",md5);
        List<Files> filesList = fileService.list(queryWrapper);
        return filesList.size()==0?null:filesList.get(0);

    }
  • 测试下载接口直接复制上面上传返回的链接,网站打开链接,便能启动下载。
http://localhost:8081/file/6b556321437b4c22ab61369cb920c142.jpg

在这里插入图片描述

4、Vue前端实现上传和下载

  1. 写Files页面

Files.vue

<template>
    <div>
        <div style="padding:10px 0">
            <el-input style="width:200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="name"></el-input>
            <el-button class="ml-5" type="primary" @click="load">搜索</el-button>
            <el-button  type="warning" @click="reset">重置</el-button>
        </div>
            <div style="margin:10px 0">
        <el-upload action="http://localhost:8081/file/upload" :show-file-list="false" :on-success="handleFileUploadSuccess" style="display:inline-block">
            <el-button type="primary" class="ml-5">上传文件<i class="el-icon-top"></i></el-button>
        </el-upload>
        <el-popconfirm
                class="ml-5"
                confirm-button-text='确定'
                cancel-button-text='我再想想'
                icon="el-icon-info"
                icon-color="red"
                title="您确定要删除这些内容吗?"
                @confirm="delBatch"
          >
          <el-button type="danger"  slot="reference">批量删除<i class="el-icon-remove-outline"></i></el-button>
          </el-popconfirm>
    </div>


    <el-table :data="tableData" border stripe :header-cell-calss-name="'headerBg'"    @selection-change="handleSelectionChange">
    <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="id" label="ID" width="80"></el-table-column>
       <el-table-column prop="name" label="文件名称" ></el-table-column>
       <el-table-column prop="type" label="文件类型" ></el-table-column>
       <el-table-column prop="size" label="文件大小(kb)" ></el-table-column>
       <el-table-column label="下载" >
        <template slot-scope="scope">
            <el-button type="primary" @click="download(scope.row.url)">下载</el-button>
        </template>
       </el-table-column>
       <el-table-column prop="enable" label="启用">
        <template slot-scope="scope">
            <el-switch v-model="scope.row.enable" active-color="#13ce66" inactive-color="#ccc" @change="changeEnable(scope.row)"></el-switch>
        </template>

       </el-table-column>
       <el-table-column label="操作" width="200" align="center">
         <template slot-scope="scope" >
          <el-popconfirm
                class="ml-5"
                confirm-button-text='确定'
                cancel-button-text='我再想想'
                icon="el-icon-info"
                icon-color="red"
                title="您确定要删除吗?"
                @confirm="handleDelete(scope.row.id)"
          >
          <el-button type="danger" slot="reference">删除<i class="el-icon-remove-outline"></i></el-button>
          </el-popconfirm>
         </template>
       </el-table-column>
     </el-table>
     <div style="padding:10px 0">
       <el-pagination
       @size-change="handleSizeChange"
       @current-change="handleCurrentChange"
         :current-page="pageNum"
         :page-sizes="[2, 4, 6, 10]"
         :page-size="pageSize"
         layout="total, sizes, prev, pager, next, jumper"
         :total="total">
       </el-pagination>
     </div>

    </div>
</template>

<script>

export default {

    name: "File",
    data(){
        return{
            tableData:[],
            name:'',
            multipleSelection:[],
            pageNum:1,
            pageSize:10,
            total:0
        }
    },
    created(){
        this.load()
    },
    methods:{
        load(){
      this.request.get("/file/page",{
        params:{
        pageNum:this.pageNum,
        pageSize:this.pageSize,
        name:this.name

      }
        }).then(res=>{
        console.log(res)
        this.tableData=res.data.records
        this.total=res.data.total 
      })
     },

     changeEnable(row){
        this.request.post("/file/update",row).then(res =>{
            if(res.code==='200'){
                this.$message.success("操作成功");
            }
        })

     },




     handleDelete(id){
      this.request.delete("/file/" + id).then(res=>{
        if(res.code==='200'){
          this.$message.success("删除成功!")
          this.load()
        }else{
          this.$message.error("删除失败!")
        }
      })
     },
     delBatch(){
      let ids=this.multipleSelection.map(v => v.id)  //把对象数组转化为id数组【1,2,3】
      this.request.post("/file/del/batch",ids).then(res=>{
        if(res.code==='200'){
          this.$message.success("批量删除成功!")
          this.load()
        }else{
          this.$message.error("批量删除失败!")
        }
      })

     },
     handleSelectionChange(val){
      this.multipleSelection=val



     },
     reset(){
      this.name=""
      this.load()

     },
    
     handleSizeChange(pageSize){
      this.pageSize=pageSize
      this.load()
     },
     handleCurrentChange(pageNum){
      this.pageNum=pageNum
      this.load()
     },
     handleFileUploadSuccess(res){
        console.log(res)
        this.load()

     },
     download(url){
        window.open(url)
     }

    }
}
</script>

<style scoped>

</style>
  1. 注册路由
  {
        path:'file',
        name:'文件管理',
        component:()=>import('../views/Files.vue')
      }

写在home的子路由下

  1. 实现增删改查功能,代码在上面Files.vue里

  2. 去后端写文件增删改查接口

FilesController

 //分页查询
    @GetMapping("/page")
    public Result findPage(@RequestParam Integer pageNum,
                           @RequestParam Integer pageSize,
                           @RequestParam(defaultValue = "") String name){
        return Result.success(fileService.findPage(pageNum,pageSize,name));

    }

    //删除
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id){
        Files files = fileService.getById(id);
        files.setIsDelete(true);
        fileService.updateById(files);
        return Result.success();
    }

    //批量删除
    @PostMapping ("/del/batch")
    public Result deleteBatch(@RequestBody List<Integer>ids){
        QueryWrapper<Files>queryWrapper=new QueryWrapper<>();
        queryWrapper.in("id",ids);
        List<Files> files = fileService.list(queryWrapper);
        for (Files file :files ) {
            file.setIsDelete(true);
            fileService.updateById(file);

        }
        return Result.success();
    }

    //更新一个字段
    @PostMapping("/update")
    public Result save(@RequestBody Files files){
        return Result.success(fileService.updateById(files));
    }


FilesService

 public IPage<Files> findPage(Integer pageNum,
                                Integer pageSize,
                                String  name) {
        QueryWrapper<Files> queryWrapper=new QueryWrapper<>();
        //查询未删除的记录
        queryWrapper.eq("is_delete",false);
        queryWrapper.orderByDesc("id");
        if (! "".equals(name)){
            queryWrapper.like("name",name);
        }
        return this.page(new Page<>(pageNum,pageSize),queryWrapper);
    }
  1. 在页面上测试上传下载,增删改查功能

在这里插入图片描述

测试功能正常,成功~

5、实现用户头像上传

  1. 首先需要在数据库user表字段中添加一个avatarUrl的字段

在这里插入图片描述

  1. 在实体类User中添加avatarUrl属性对应
public class User {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String username;
//密码可以不展示, @JsonIgnore 意思就是给前端传数据时忽略某个字段
    @JsonIgnore
    private String password;
    private String nickname;
    private String email;
    private String phone;
    private String address;
    private String avatarUrl;
}
  1. 在前端Header页面中修改,昵称旁边要有头像的位置

Header.vue

 <div style="display:inline-block">
            <img :src="user.avatarUrl" alt=""
                style="width:30px;border-radius: 50%;position:relative;top: 10px; right: 5px">
            <span>{{user.nickname}}</span><i class="el-icon-arrow-down" style="margin-left:5px"></i>

        </div>
  1. 在Person.vue里,添加上传头像功能
<template>
   <el-card style="width:500px;padding:20px;">
    <el-form label-width="80px" size="small">
      <el-upload
        class="avatar-uploader"
        action="http://localhost:8081/file/upload"
        :show-file-list="false"
        :on-success="handleAvatarSuccess">
        <img v-if="form.avatarUrl" :src="form.avatarUrl" class="avatar">
        <i v-else class="el-icon-plus avatar-uploader-icon"></i>
      </el-upload>
          <el-form-item label="用户名" >
            <el-input v-model="form.username" autocomplete="off" disabled ></el-input>
          </el-form-item>
          <el-form-item label="昵称" >
            <el-input v-model="form.nickname" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="邮箱" >
            <el-input v-model="form.email" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="电话" >
            <el-input v-model="form.phone" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item label="地址" >
            <el-input v-model="form.address" autocomplete="off"></el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="save">确 定</el-button>
          </el-form-item>
        </el-form>
   </el-card>
</template>

<script>
export default{
    name:"Person",
    data(){
        return{
            form:{},
            user:localStorage.getItem("user")?JSON.parse(localStorage.getItem("user")):{}
        }
    },
    created(){
       this.getUser().then(res=>{
        console.log(res)
        this.form=res
          
        })
    },
    methods:{
      async getUser(){
        return (await this.request.get("/user/username/"+this.user.username)).data
      },
      save(){
        this.request.post("/user",this.form).then(res=>{
          if(res.code==='200'){
            this.$message.success("保存成功!")
            //更新浏览器存储的用户信息
            this.getUser().then(res=>{
              res.token=JSON.parse(localStorage.getItem("user")).token
              localStorage.setItem("user",JSON.stringify(res))
            })
          }else{
            this.$message.error("保存失败!")
        }
      })

    },
    handleAvatarSuccess(res){
      this.form.avatarUrl=res

    }


    }
}

</script>

<style>

  .avatar-uploader{
    text-align: center;
    padding-bottom: 10px;
  }
  .avatar-uploader .el-upload {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
  }
  .avatar-uploader .el-upload:hover {
    border-color: #409EFF;
  }
  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 138px;
    height: 138px;
    line-height: 138px;
    text-align: center;
  }
  .avatar {
    width: 138px;
    height: 138px;
    display: block;
  }
</style>
  1. 测试

在这里插入图片描述

上传一张新的图片,并保存,刷新页面,看头像是否更新。更新则成功完成!(记住要和数据库files表中已经有的图片不一样)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/134865.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Detection of Individual Tree Crowns in Airborne Lidar Data

Abstract 激光扫描提供了一种收集林分信息的好方法。本文介绍了一种在落叶林和混合温带森林中的小足迹光检测和测距&#xff08;激光雷达&#xff09;数据中自动描绘单棵树的方法。在光栅化激光数据中&#xff0c;可能的树顶用局部最大值滤波器检测。之后&#xff0c;结合浇注…

【Bootstrap】基础知识和环境配置

目录 一、Bootstrap基础 1. 概念 2. 特点 3. 组成 3.1 基本结构 3.2 丰富的CSS样式库 3.3 布局组件 3.4 插件 二、bootstrap的环境配置 1. 在页面中引入本地文件 2. 使用CDN加速器 一、Bootstrap基础 1. 概念 Bootstrap是一个基于HTML、CSS和JavaScript语言编写的…

动手学习深度学习-《自动求导》

向量链式求导法则 标量链式法则&#xff1a; 扩展到向量&#xff1a; yyy是关于标量uuu的一个标量&#xff0c;x\bf{x}x是一个向量 yyy是关于向量u\bf{u}u的一个标量&#xff0c;x\bf{x}x是一个向量 y\bf{y}y是关于向量u\bf{u}u的一个向量&#xff0c;x\bf{x}x是一个向量…

Nginx+keepalived 实现高可用,防盗链及动静分离配置详解

一、Nginx Rewrite 规则 1. Nginx rewrite规则 Rewrite规则含义就是某个URL重写成特定的URL&#xff08;类似于Redirect&#xff09;&#xff0c;从某种意义上说为了美观或者对搜索引擎友好&#xff0c;提高收录量及排名等。 语法&#xff1a; rewrite <regex> <r…

Oracle11g彻底卸载教程(详细版)

目前项目结束,暂时不需要Oracle了,为清理电脑并便于后续重装,现记录彻底卸载的步骤如下: 1、按下“Windows”+“R”键,在运行窗中,输入compmgmt.msc,进入计算机管理------>服务,停止所有Oracle服务(对于状态为 正在进行 的服务,右键------>停止)。 2、在开始…

遗传算法解决旅行商问题(TSP)

遗传算法解决旅行商问题 作者&#xff1a;Cukor丘克环境&#xff1a;MatlabR202a vscode 问题描述 旅行商问题&#xff08;TSP&#xff09;. 一个商人欲从自己所在的城市出发&#xff0c;到若干个城市推销商品&#xff0c;然后回到其所在的城市。如何选择一条周游路线&…

SpringBoot3初体验 - 第457篇

历史文章&#xff08;文章累计450&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 5个月的精华&#xff1a;Spring/SpringB…

【JavaScript】多维数组及数组扁平化

文章目录【JavaScript】多维数组及数组扁平化一. 概念二. 数组扁平化方式一&#xff1a;使用flat()方法方式二&#xff1a;使用递归方式三&#xff1a;使用reduce()方式处理三. 对象数组的联合操作【JavaScript】多维数组及数组扁平化 一. 概念 (1) 一维数组&#xff1a;数组…

深度学习Week13-火灾温度预测(LSTM)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f366; 参考文章&#xff1a;第R2周&#xff1a;LSTM-火灾温度预测&#xff08;训练营内部可读&#xff09;&#x1f356; 作者&#xff1a;K同学啊任务说明&#xff1a;数据集中提供了火灾温度&…

c++知识点总结

文章目录1.引用2. 重载3. extern “C”4.构造函数5.析构函数6.类和对象7.面向对象模型8.继承9.多态10.函数模板11.类模板12.STL1.引用 不要返回局部变量的引用&#xff0c;调用函数执行后局部变量会销毁 2. 重载 是C多态的特性&#xff08;静态多态&#xff09;。同一个函数名代…

正点原子STM32(基于HAL库)

正点原子B站视频地址&#xff1a;https://www.bilibili.com/video/BV1bv4y1R7dp?p1&vd_sourcecc0e43b449de7e8663ca1f89dd5fea7d 目录单片机简介Cortex-M介绍初识STM32STM32命名规则STM32 选型STM32 设计数据手册最小系统IO 分配STM32启动过程分析启动模式启动文件分析启动…

基于彩色的图像分割

图像分割就是把图像分成若干个特定的、具有独特性质的区域并提出感兴趣目标的技术和过程。它是由图像处理到图像分析的关键步骤。现有的图像分割方法主要分以下几类&#xff1a;基于阈值的分割方法、基于区域的分割方法、基于边缘的分割方法以及基于特定理论的分割方法等。从数…

小程序03/ uni-app认识目录结构 、uni-app应用生命周期 和 生命周期钩子、uni-app页面生命周期 和 生命周期钩子

一.uni-app认识目录结构 二.uni-app应用生命周期 和 生命周期钩子 位置: uni-app 在App.vue中监听 在页面监听无效 说明: App.vue是uni-app的主组件 所有页面都是在App.vue 下进行切换的、是页面入口文件 但是App.vue 本身不是页面 这里不能编写视图元素 也就是没有<tmpl…

Git(一) - Git 概述

01_尚硅谷_Git_课程介绍_哔哩哔哩_bilibili Git 是一个免费的、开源的分布式版本控制系统&#xff0c;可以高效的处理从小型到大型的各种项目。 一、何为版本控制 版本控制是一种记录文件内容变化&#xff0c;以便将来查阅特定版本修订情况的系统。 版本控制其实最主要的是可以…

java 瑞吉外卖优化day2 Nginx

Nginx概述 下载与安装 可以到Nginx官方网站下载Nginx的安装包&#xff0c;地址为&#xff1a;https://nginx.org/en/download.html 安装过程&#xff1a; 1、安装依赖包 yum -y install gcc pcre-devel zlib-devel openssl openssl-devel 先yum install wget &#xff0c;…

随机数 - 时间种子的方案与实践

1.应用场景 主要弄清楚设置随机数种子的方法&#xff0c;可用于游戏开发当中的时间种子从而产生合理的随机数&#xff0c;避免出现bug。 2.学习/操作 1.文档阅读 07 | 带你快速上手 Lua-极客时间 2.整理输出 2.1 什么是种子 现在很多朋友下载东西时都会用bt种子文件&#xff0…

5分钟带你学习 linux 收发邮件步骤详解 at命令详解 crontab命令详解 附加at crontab命令练习

linux 收发邮件步骤详解 1.安装软件yum install mailx -yyum install sendmail -y 2.启动服务sendmailsystemctl start sendmail 3.更改配置vim /etc/mail.rc at命令详解 实例&#xff1a; crontab命令详解 实例&#xff1a; linux 收发邮件步骤详解 1.安装软件 yum…

拜占庭将军问题

前言 在分布式系统中交换信息, 部分成员可能出错导致发送了错误的信息, 在这种情况下如何达成共识. 这就是拜占庭将军问题所要解决的. 问题的简要描述如下: 3个军队协同作战(为了简单易懂, 以3个军队描述)每支军队的作战策略有两种"进攻"和"撤退"每个军…

SparkLaunch提交Spark任务到Yarn集群

SparkLaunch提交任务1.提交Spark任务的方式2.SparkLaunch 官方接口3.任务提交流程及实战1.提交Spark任务的方式 通过Spark-submit 提交任务通过Yarn REST Api提交Spark任务通过Spark Client Api 的方式提交任务通过SparkLaunch 自带API提交任务基于Livy的方式提交任务&#xf…

深拷贝浅拷贝的区别?如何实现一个深拷贝

一、数据类型存储 前面文章我们讲到&#xff0c;JavaScript中存在两大数据类型&#xff1a; 基本类型引用类型 基本类型数据保存在在栈内存中 引用类型数据保存在堆内存中&#xff0c;引用数据类型的变量是一个指向堆内存中实际对象的引用&#xff0c;存在栈中 二、浅拷贝…