JavaWeb合集23-文件上传

news2024/11/26 2:02:08

二十三 、 文件上传

实现效果:用户点击上传按钮、选择上传的头像,确定自动上传,将上传的文件保存到指定的目录中,并重新命名,生成访问链接,返回给前端进行回显。

1、前端实现

vue3+AntDesignVue实现

<template>
       <!--图片回显-->
          <a-avatar :size="100">
            <template #icon>
              <img :src="userStore.userInfo.avatar+'?' + new Date().getTime()" alt="">
            </template>
          </a-avatar>
          <!--图片上传按钮 -->
           <!--showUploadList=fales 不显示上传列表;:multiple="false" 只上传1个;accept=".png,.jpg" 限制文件格式-->
          <a-upload 
           name="file"
          :file-list="fileList"
          :showUploadList="false"
          :beforeUpload="beforeUpload" 
          :onChange="handleChange" 
          :multiple="false"  
          accept=".png,.jpg"
           >
            <a-button class="mt-4">
               上传头像
            </a-button>
          </a-upload>

</template>

<script setup lang="ts">
import type { UnwrapRef } from 'vue';
import { updateUserAvatarApi } from '~/api/file/file.ts';


const { t } = useI18n()
//文件列表
const fileList = ref([]);

//
async function beforeUpload(file){
 //文件类型,限制,可以不用写,因为,在accept=".png,.jpg" 已经限制了
var fileName = file.name.substring(file.name.lastIndexOf('.') + 1)
if (fileName!='png' && fileName!='jpg') {
  message.error('文件类型必须是png或jpg!')
return false
}

//文件大小(3M=3*1024KB*1024字节)
var fileSize=file.size;
if(fileSize > 3*1024*1024){
 message.error("图片大小不能大于3M");
 return false
}

try {  
    const formData = new FormData(); 
     formData.append('file', file); // 将文件添加到 FormData 对象中  
    // 假设updateUserAvatarApi返回的是一个Promise,且解析为包含fileUrl的对象  
    const response = await updateUserAvatarApi(formData);  
    if(response.data.code==1){
      fileList.value=[];  //清空文件列表(没用因为在beforeUpload中,我直接showUploadList="false"不显示列表)
      userStore.userInfo.avatar=response.data.data;
      
     message.success('头像上传成功');
    
    }else{
      message.error('头像上传失败,请重试');  
    }
     
  } catch (error) {  
    // 处理上传失败的情况  
    message.error(error);  
  }  
  // 返回false以阻止<a-upload>的默认上传行为  
  return false;  

}

// 处理文件上传或移除后的逻辑  
function handleChange(info) {  
  // info.fileList 是更新后的文件列表  
  // 但由于限制了multiple为false,所以这里fileList应该始终只有一个文件或为空  
  fileList.value = info.fileList.slice(-1);  
}  

</script>

请求函数

// 后面的方法是用户自己头像
export function updateUserAvatarApi(param: any) {
    return usePost<FileUrl>('/upload/uploadUserAvater', param, {
      // 设置为false的时候不会携带token
      token: true,
      // 开发模式下使用自定义的接口
      customDev: true,
      // 是否开启全局请求loading
      loading: false,
      // 设置请求头
    headers: {
      'Content-Type': 'multipart/form-data'
    }
    })
  }

  export interface FileUrl {
    data: string
  }

2、后端实现

配置拦截器的静态资源映射,方便对上传后的文件进行访问

/**
 * 配置拦截器的静态资源映射
 */
@Slf4j
@Configuration
public class ResourConfigure implements WebMvcConfigurer {
   
     // 静态资源映射
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        /**
         * 资源映射路径
         * addResourceHandler:访问映射路径
         * addResourceLocations:资源绝对路径
         */
        String osName=System.getProperty("os.name");
        String fileUploadResources="/static/**";
        String win="win";
        if(osName.toLowerCase().startsWith(win)){
            ApplicationHome applicationHome=new ApplicationHome(this.getClass());
            String pre="file:"+applicationHome.getDir().getParentFile().getParentFile()+ "\\src\\main\\resources\\static\\";

            registry.addResourceHandler(fileUploadResources)
                    .addResourceLocations(pre);
        }else {
            ApplicationHome applicationHome=new ApplicationHome(this.getClass());
            String pre="file:"+applicationHome.getDir().getParentFile().getParentFile()+ "/src/main/resources/static/";
            registry.addResourceHandler(fileUploadResources)
                    .addResourceLocations(pre);
        }
    }

}

写相关配置,在application.properties中,方便生成访问链接

#根据自己的需求进行修改即可
#端口号
server.port=8080   
#服务地址
server.address=localhost  
#访问路径
server.servlet.context-path=/    
#限制单个文件的上传大小
spring.servlet.multipart.max-file-size=5MB
#限制整个请求的最大大小
spring.servlet.multipart.max-request-size=5MB

创建文件上传工具类,方便对文件上传进行操作。

在此之前,确保这个目录的存在:(\src\main\resources\static\)可根据自己需求进行修改,在工具类中

/**
 * 文件上传工具类
 */
@Component
public class FileUpload {

    @Value("${server.servlet.context-path}")
    private String contextPath;

    @Value("${server.port}")
    private String serverPort;

    @Value("${server.address}")
    private String serverAddress;

    @Autowired
    private ServletContext servletContext;


    public String uploadFile(MultipartFile file, String folder) {
        // 获取图片的原始名字
        String originalFilename = file.getOriginalFilename();

        if (originalFilename == null || originalFilename.isEmpty()) {
            throw new IllegalArgumentException("文件名不能为空");
        }

        // 获取文件的后缀和新文件名
        String ext = "." + originalFilename.substring(originalFilename.lastIndexOf('.') + 1);
        String uuid = UUID.randomUUID().toString().replace("-", "");
        String fileName = uuid + ext;

        // 构建目标文件路径
        String pre = getResourcePath(folder);
        String filePath = pre + fileName;


        // 上传图片
        try {
            file.transferTo(new File(filePath));
            // 返回访问链接
            return getAccessPath(folder, fileName);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public String uploadFile(MultipartFile file, String folder, String fileName) {
        // 获取图片的原始名字
        String originalFilename = file.getOriginalFilename();

        if (originalFilename == null || originalFilename.isEmpty()) {
            throw new IllegalArgumentException("文件名不能为空");
        }

        // 获取文件的后缀和新文件名
        String ext = "." + originalFilename.substring(originalFilename.lastIndexOf('.') + 1);
        fileName = fileName + ext;

        // 构建目标文件路径
        String pre = getResourcePath(folder);
        String filePath = pre + fileName;

        // 上传图片
        try {
            file.transferTo(new File(filePath));
            // 返回访问链接
            return getAccessPath(folder, fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
    
    //获取上传路径
    private String getResourcePath(String folder) {
        // 获取操作系统的名称
        String osName = System.getProperty("os.name");
        String win = "win";

        // 获取项目的根目录
        String projectRoot = System.getProperty("user.dir");

        // 根据操作系统生成正确的路径
        String staticPath;
        if (osName.toLowerCase().startsWith(win)) {
            //windos系统
            staticPath = projectRoot + "\\src\\main\\resources\\static\\" + folder + "\\";
        } else {
            //Linux系统
            staticPath = projectRoot + "/src/main/resources/static/" + folder + "/";
        }


        // 如果目录不存在,就创建目录
        File dir = new File(staticPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        return staticPath;
    }

    //构建访问路径
    private String getAccessPath(String folder, String fileName) {
        // 构建访问路径
        return "http://" + serverAddress + ":" + serverPort + contextPath + "static/" + folder + "/" + fileName;
    }
}

可以设置一个常量类,来指定上传到的目录

public class FileUploadFolder {
    public static final String USER_AVATER_FOLDER = "user_avatar";
    public static final String  ACTIVITY_FOLDER = "activity";
}

后端接口编写:Controller层,根据自己的项目来即可

@Tag(name="文件上传接口")
@RestController
@RequestMapping("/upload")
@Slf4j
public class UploadController {

    @Autowired
    private UserService userService;


    /**
     * 修改用户头像
     * @param file
     * @return
     */
    @Operation(summary = "修改用户头像")
    @PostMapping("/uploadUserAvater")
    public ResponseResult uploadUserAvater(@RequestParam("file") MultipartFile file){
        if(file.isEmpty()){
            return ResponseResult.error("头像为空,请重新选择");
        }
        String imgUrl = userService.uploadUserAvater(file);
        if(imgUrl==null){
            return ResponseResult.error("头像上传失败");
        }
        return ResponseResult.success(imgUrl);
    }

}

Service层:根据自己的项目来即可

@Slf4j
@Service
@Transactional
public class UserServiceImpl implements UserService {
    //根据自己的项目来,注入对应的Bean
    @Autowired
    private final UserMapper userMapper;

     //注入写好的工具类
    @Autowired
    private FileUpload fileUpload;

    /**
     * 修改用户头像
     * @param file
     * @return
     */
    @Override
    public String uploadUserAvater(MultipartFile file) {
        // 获取用户获取用户信息(根据自己的项目来,获取用户ID,来查询用户修改头像链接)
        Long uid = Long.parseLong(StpUtil.getLoginId().toString());
        UserInfo userInfo = userMapper.getUserById(uid);
        // 调用文件上传工具类,传入:文件,保存到的文件夹,文件名
        //设置文件名是为了替换旧的头像文件
            String imgUrl= fileUpload.uploadFile(file, FileUploadFolder.USER_AVATER_FOLDER,userInfo.getUsername());
            if(imgUrl!=null) {
                userInfo.setAvatar(imgUrl);
                userMapper.updateById(userInfo);
                log.info("头像上传成功:" + imgUrl);
                //返回生成的链接
                return imgUrl;
            }
      return null;
    }
    
}

3、测试与效果

上传后的图片

在这里插入图片描述

生成的访问链接,存入到数据库的样子

在这里插入图片描述

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

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

相关文章

Java项目实战II基于Spring Boot的药店管理系统的设计与实现(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 随着医疗行业的快速发展和人们对健康需…

「撸一手好代码」设计模式之接口隔离原则

「撸一手好代码」设计模式之接口隔离原则 什么是接口隔离原则 接口隔离原则&#xff08;Interface Segregation Principle, ISP&#xff09;指出&#xff0c;客户端不应该依赖它不使用的接口。换句话说&#xff0c;一个类对另一个类的依赖应该建立在最小的接口上。接口的设计应…

在 WPF 中,绑定机制是如何工作的?WPF数据绑定机制解析

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;数据绑定机制是其核心功能之一&#xff0c;广泛用于连接应用程序的UI&#xff08;用户界面&#xff09;和应用程序的业务逻辑层。数据绑定允许你将UI元素与数据源&#xff08;如对象、集合或其他数…

线性代数:Matrix2x2和Matrix3x3

今天整理自己的框架代码&#xff0c;将Matrix2x2和Matrix3x3给扩展了一下&#xff0c;发现网上unity数学计算相关挺少的&#xff0c;所以记录一下。 首先扩展Matrix2x2&#xff1a; using System.Collections; using System.Collections.Generic; using Unity.Mathemati…

字节青训-小F的永久代币卡回本计划、

目录 一、小F的永久代币卡回本计划 问题描述 测试样例 解题思路&#xff1a; 问题理解&#xff1a; 数学公式&#xff1a; 代码实现&#xff1a; 最终代码&#xff1a; 运行结果&#xff1a; 二、构造特定数组的逆序拼接 问题描述 测试样例 解题思路&#xff1a;…

PGMP-串串0203 项目集管理绩效域战略一致性

1.项目集管理绩效域 2.战略一致性 战略一致性包含内容商业论证BC项目集章程项目集路线图环境评估项目集风险管理策略 前期formulation sub-phaseplanning sub-phase组织的战略计划项目集风险管理策略项目集管理计划商业论证BC项目集章程项目集路线图环境评估

使用ThorUi

摘要&#xff1a; 官网 今天遇到一个老项目&#xff0c;使用的是ThorUi组件库&#xff01;之前没有用过这组件库&#xff0c;所以记录一下不同框架是使用情况&#xff01; ThorUI 是一个基于 Thorium 的 UI 框架&#xff0c;用于构建跨平台的桌面应用程序。如果你打算使用 Thor…

【activiti工作流源码集成】springboot+activiti+mysql+vue+redis工作流审批流集成整合业务绑定表单流程图会签驳回

工作流集成实际项目案例&#xff0c;demo提供 源码获取方式&#xff1a;本文末个人名片直接获取。 前言 activiti工作流引擎项目&#xff0c;企业erp、oa、hr、crm等企事业办公系统轻松落地&#xff0c;请假审批demo从流程绘制到审批结束实例。 一、项目形式 springbootvue…

【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案

作者&#xff1a;CSDN-PleaSure乐事 欢迎大家阅读我的博客 希望大家喜欢 使用环境&#xff1a;WebStorm 目录 问题概述 原因 解决方案 解决方法 潜在问题修改 最终效果呈现 额外内容 管理员界面路由配置 WebStorm背景更换 法一&#xff1a; 法二&#xff1a; 问题概…

布谷直播源码部署服务器关于数据库配置的详细说明

布谷直播源码搭建部署配置接口数据库 /public/db.php&#xff08;2019年8月后的系统在该路径下配置数据库&#xff0c;老版本继续走下面的操作&#xff09; 在项目代码中执行命令安装依赖库&#xff08;⚠️注意&#xff1a;如果已经有了vendor内的依赖文件的就不用执行了&am…

Spring中的过滤器和拦截器

Spring中的过滤器和拦截器 一、引言 在Spring框架中&#xff0c;过滤器&#xff08;Filter&#xff09;和拦截器&#xff08;Interceptor&#xff09;是实现请求处理的两种重要机制。它们都基于AOP&#xff08;面向切面编程&#xff09;思想&#xff0c;用于在请求的生命周期…

Day16二叉树的中序遍历

给定一个二叉树的根节点 root &#xff0c;返回它的中序遍历 。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(i…

RT-DETR实战TT100K中国交通标志识别

本文采用RT-DETR作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。RT-DETR以其高效的实时检测能力&#xff0c;在多个目标检测任务中展现出卓越性能。本研究针对TT100K交通标志数据集进行训练和优化&#xff0c;该数据集包含丰富的TT100K交…

图像信号处理器(ISP,Image Signal Processor)详解

简介&#xff1a;个人学习分享&#xff0c;如有错误&#xff0c;欢迎批评指正。 图像信号处理器&#xff08;ISP&#xff0c;Image Signal Processor&#xff09; 是专门用于处理图像信号的硬件或处理单元&#xff0c;广泛应用于图像传感器&#xff08;如 CMOS 或 CCD 传感器&a…

js基础篇笔记 (万字速通)

此笔记来自于黑马程序员,仅供笔者复习 JavaScript 基础 - 第1天 了解变量、数据类型、运算符等基础概念&#xff0c;能够实现数据类型的转换&#xff0c;结合四则运算体会如何编程。 体会现实世界中的事物与计算机的关系理解什么是数据并知道数据的分类理解变量存储数据的“容…

【GPTs】EmojiAI:轻松生成趣味表情翻译

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | GPTs应用实例 文章目录 &#x1f4af;GPTs指令&#x1f4af;前言&#x1f4af;EmojiAI主要功能适用场景优点缺点 &#x1f4af;小结 &#x1f4af;GPTs指令 中文翻译&#xff1a; 此 GPT 的主要角色是为英文文本提供幽默…

H.264/H.265播放器EasyPlayer.js无插件H5播放器关于WASM的压缩优化

在当今的Web开发领域&#xff0c;流媒体播放器的性能和效率至关重要&#xff0c;尤其是在处理大型视频文件和高分辨率视频流时。EasyPlayer.js RTSP播放器作为一款先进的流媒体播放器&#xff0c;它在WebAssembly&#xff08;WASM&#xff09;的压缩优化方面表现出色&#xff0…

Unity Shader分段式血条

Unity Shader分段式血条 前言项目ASE连线 前言 要给单位加一个类似LOL的分段式血条&#xff0c;用ASE实现并记录一下。里面加了旋转和颜色的渐变。 项目 ASE连线

基于 STM32 的天气时钟项目中添加天气数据的网络获取功能

基于 STM32 的天气时钟项目中添加天气数据的网络获取功能&#xff0c;您需要确保您的开发环境具备网络连接能力。这里以 ESP8266 Wi-Fi 模块为例&#xff0c;详细说明如何实现网络获取天气数据的功能。 1. 硬件连接 连接 ESP8266 模块 请参考以下连接方式&#xff0c;将 ESP82…

element-plus按需引入报错AutoImport is not a function

官网文档&#xff1a;快速开始 | Element Plus webpack配置 // webpack.config.js const AutoImport require(unplugin-auto-import/webpack) const Components require(unplugin-vue-components/webpack) const { ElementPlusResolver } require(unplugin-vue-components…