【前后端的那些事】15min快速实现图片上传,预览功能(ElementPlus+Springboot)

这篇具有很好参考价值的文章主要介绍了【前后端的那些事】15min快速实现图片上传,预览功能(ElementPlus+Springboot)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言:最近写项目,发现了一些很有意思的功能,想写文章,录视频把这些内容记录下。但这些功能太零碎,如果为每个功能都单独搭建一个项目,这明显不合适。于是我想,就搭建一个项目,把那些我想将的小功能全部整合到一起。实现 搭一次环境,处处使用。

本文主要实现以下功能

  1. 图片上传

环境搭建
文章链接

已录制视频
视频链接

仓库地址
https://github.com/xuhuafeifei/fgbg-font-and-back.git

Element Plus + SpringBoot实现图片上传,预览,删除

效果展示
  • 提交样式
    【前后端的那些事】15min快速实现图片上传,预览功能(ElementPlus+Springboot),spring boot,后端,vue,图片上传

  • 放大预览

【前后端的那些事】15min快速实现图片上传,预览功能(ElementPlus+Springboot),spring boot,后端,vue,图片上传

  • 成功提交后端
    【前后端的那些事】15min快速实现图片上传,预览功能(ElementPlus+Springboot),spring boot,后端,vue,图片上传

  • 访问url

【前后端的那些事】15min快速实现图片上传,预览功能(ElementPlus+Springboot),spring boot,后端,vue,图片上传

  • 后端存储
    【前后端的那些事】15min快速实现图片上传,预览功能(ElementPlus+Springboot),spring boot,后端,vue,图片上传

  • 根据url下载/访问图片

【前后端的那些事】15min快速实现图片上传,预览功能(ElementPlus+Springboot),spring boot,后端,vue,图片上传

1. 后端代码

1.1 controller
import com.fgbg.common.utils.R;
import com.fgbg.demo.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;

@RestController
@RequestMapping("/common/file")
public class FileController {
    @Autowired
    @Qualifier("localFileService")
    private FileService fileService;

    /**
     * 上传接口
     */
    @RequestMapping("/upload")
    public R upload(@RequestParam("image") MultipartFile file) throws IOException {
        String url = fileService.uploadFile(file, UUID.randomUUID().toString().substring(0, 10)
                + "-" + file.getOriginalFilename());
        return R.ok().put("data", url);
    }

    /**
     * 下载接口
     */
    @RequestMapping("/download/{fileName}")
    public void download(@PathVariable("fileName") String fileName, HttpServletRequest request, HttpServletResponse response) {
        fileService.downloadFile(fileName, request, response);
    }

    /**
     * 删除接口
     */
    @RequestMapping("/delete")
    public R deleteFile(@RequestParam String fileName) {
        boolean flag = fileService.deleteFile(fileName);
        return R.ok().put("data", flag);
    }
}

1.2 service

tip: 文件上传存储有多种解决方案,比如minio,阿里云…

笔者考虑到编写容易程度与文章核心解决问题,采用了最原始的存储方法,即本地存储。以后端所在服务器为存储容器,将前端上传的图片以FileIO的形式进行存储。

考虑到有多种存储方式,读者可以实现FileService接口,自行编写impl类,以达到不同的文件存储的具体实现方式

import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public interface FileService {
    /**
     * 上传图片, 返回url
     */
    String uploadFile(MultipartFile file, String fileName) throws IOException;

    /**
     * 下载图片
     */
    void downloadFile(String fileName, HttpServletRequest request, HttpServletResponse response);

    /**
     * 删除图片
     */
    boolean deleteFile(String fileName);
}

impl

import com.fgbg.demo.service.FileService;
import org.apache.commons.io.FileUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * 基于本地的文件管理服务
 */
@Service("localFileService")
public class LocalFileServiceImpl implements FileService {
    /**
     * 文件访问域名(请求下载的接口)
     */
    private static final String DOMAIN = "http://localhost:9005/api_demo/common/file/download/";

    /**
     * 文件物理存储位置
     */
    private static final String STORE_DIR = "E:\\B站视频创作\\前后端项目构建-小功能实现\\代码\\backend\\src\\main\\resources\\pict\\";

    /**
     * 上传图片, 返回url
     *
     * @param file
     * @param fileName
     */
    @Override
    public String uploadFile(MultipartFile file, String fileName) throws IOException {
        // 获取文件流
        InputStream is = file.getInputStream();
        // 在服务器中存储文件
        FileUtils.copyInputStreamToFile(is, new File(STORE_DIR + fileName));
        // 返回图片url
        String url = DOMAIN + fileName;
        System.out.println("文件url: " + url);
        return url;
    }

    /**
     * 下载图片
     *
     * @param fileName
     */
    @Override
    public void downloadFile(String fileName, HttpServletRequest request, HttpServletResponse response) {
        // 获取真实的文件路径
        String filePath = STORE_DIR + fileName;
        System.out.println("++++完整路径为:"+filePath);

        try {
            // 下载文件
            // 设置响应头
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName);

            // 读取文件内容并写入输出流
            Files.copy(Paths.get(filePath), response.getOutputStream());
            response.getOutputStream().flush();
        } catch (IOException e) {
            response.setStatus(404);
        }
    }

    /**
     * 删除图片
     *
     * @param fileName
     */
    @Override
    public boolean deleteFile(String fileName) {
        // 获取真实的文件路径
        String filePath = STORE_DIR + fileName;
        System.out.println("++++完整路径为:"+filePath);

        File file = new File(filePath);
        return file.delete();
    }
}

2. 前端代码

2.1 路由创建

/src/router/modules/file.ts

const { VITE_HIDE_HOME } = import.meta.env;
const Layout = () => import("@/layout/index.vue");

export default {
  path: "/file",
  name: "file",
  component: Layout,
  redirect: "/pict",
  meta: {
    icon: "homeFilled",
    title: "文件",
    rank: 0
  },
  children: [
    {
      path: "/pict",
      name: "pict",
      component: () => import("@/views/file/pict.vue"),
      meta: {
        title: "图片",
        showLink: VITE_HIDE_HOME === "true" ? false : true
      }
    }
  ]
} as RouteConfigsTable;
2.2 api接口

tip:

  • 文件上传只能用post
  • 前端部分图片封装为FormData对象
  • 请求头标明"Content-Type": "multipart/form-data"
import { http } from "@/utils/http";
import { R, baseUrlApi } from "./utils";

/** upload batch */
export const uploadBatch = (data: FormData) => {
  return http.request<R<any>>("post", baseUrlApi("common/file/uploadList"), {
    data,
    headers: {
      "Content-Type": "multipart/form-data"
    }
  });
};

/** upload */
export const upload = (data: FormData) => {
  return http.request<R<any>>("post", baseUrlApi("common/file/upload"), {
    data,
    headers: {
      "Content-Type": "multipart/form-data"
    }
  });
};
2.2 文件创建

/src/views/file/pict.vue

tip:

  • 图片封装为FormData

  • formdata添加图片信息时,使用的是append()方法. append(name: string, value: string | Blob)

  • append的第一个参数,对应的是后端@RequestParam("xxx") MultipartFile file中xxx的值,本文中后端批量上传接口,xxx值为’imageList’

  • Element Plus上传图片,图片数据中都会有一个新的字段数据raw,这个数据我们就理解成文件本身。像后端提交数据提交的也是raw本身,而非其余额外数据

    【前后端的那些事】15min快速实现图片上传,预览功能(ElementPlus+Springboot),spring boot,后端,vue,图片上传

  • append第二个参数,提交的是fileList中每个文件元素的raw属性s数据

<template>
  <el-upload
    v-model:file-list="fileList"
    list-type="picture-card"
    multiple
    :auto-upload="false"
    :on-preview="handlePictureCardPreview"
    :on-remove="handleRemove"
  >
    <el-icon><Plus /></el-icon>
  </el-upload>

  <el-dialog v-model="dialogVisible">
    <img w-full :src="dialogImageUrl" alt="Preview Image" />
  </el-dialog>
  <el-button @click="submit">提交</el-button>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import { Plus } from "@element-plus/icons-vue";
import { uploadBatch } from "/src/api/file.ts";
import type { UploadProps } from "element-plus";
import { ElMessage } from "element-plus";

const submit = () => {
  console.log(fileList.value);
  // 封装formData
  const data = new FormData();
  // forEach遍历的时fileList.value, 所有element不需要.value去除代理
  fileList.value.forEach(element => {
    data.append("imageList", element.raw);
  });
  uploadBatch(data).then(res => {
    console.log(res);
    if (res.code === 0) {
      ElMessage.success("上传成功");
    } else {
      ElMessage.error("上传失败: " + res.msg);
    }
  });
};

const fileList = ref();

const dialogImageUrl = ref("");
const dialogVisible = ref(false);

const handleRemove: UploadProps["onRemove"] = (uploadFile, uploadFiles) => {
  console.log(uploadFile, uploadFiles);
};

const handlePictureCardPreview: UploadProps["onPreview"] = uploadFile => {
  dialogImageUrl.value = uploadFile.url!;
  dialogVisible.value = true;
};
</script>

3. 前端上传组件封装

如果没有组件封装需求,那就不需要修改代码。
组件封装视频链接

tip: 提交逻辑交由父组件实现

child.vue

<template>
  <el-upload
    v-model:file-list="localFileList"
    list-type="picture-card"
    multiple
    :auto-upload="false"
    :on-preview="handlePictureCardPreview"
    :on-remove="handleRemove"
  >
    <el-icon><Plus /></el-icon>
  </el-upload>

  <el-dialog v-model="dialogVisible">
    <img w-full :src="dialogImageUrl" alt="Preview Image" />
  </el-dialog>
</template>

<script lang="ts" setup>
import { ref, watch } from "vue";
import { Plus } from "@element-plus/icons-vue";
import type { UploadProps } from "element-plus";

// 定义数据
const props = defineProps({
  fileList: {
    type: Array,
    default: () => []
  }
});

// 将父组件的数据拆解为子组件数据
const localFileList = ref(props.fileList);

// 监听localFileList, 跟新父组件数据
watch(
  localFileList,
  newValue => {
    emits("update:fileList", newValue);
  },
  {
    deep: true
  }
);

// 定义组件事件, 跟新fileList
const emits = defineEmits(["update:fileList"]);

const dialogImageUrl = ref("");
const dialogVisible = ref(false);

const handleRemove: UploadProps["onRemove"] = (uploadFile, uploadFiles) => {
  console.log(uploadFile, uploadFiles);
};

const handlePictureCardPreview: UploadProps["onPreview"] = uploadFile => {
  dialogImageUrl.value = uploadFile.url!;
  dialogVisible.value = true;
};
</script>

父组件

<script setup lang="ts">
import Child from "./component/child.vue";
import { ref } from "vue";
import { ElMessage } from "element-plus";
import { uploadBatch } from "/src/api/file.ts";

const fileList = ref();

const submit = () => {
  console.log(fileList.value);
  // 封装formData
  const data = new FormData();
  // forEach遍历的时fileList.value, 所有element不需要.value去除代理
  fileList.value.forEach(element => {
    data.append("imageList", element.raw);
  });
  uploadBatch(data).then(res => {
    console.log(res);
    if (res.code === 0) {
      ElMessage.success("上传成功");
    } else {
      ElMessage.error("上传失败: " + res.msg);
    }
  });
};
</script>

<template>
  <Child v-model:fileList="fileList" />
  <el-button @click="submit">提交</el-button>
</template>

<style lang="scss" scoped></style>

效果
【前后端的那些事】15min快速实现图片上传,预览功能(ElementPlus+Springboot),spring boot,后端,vue,图片上传文章来源地址https://www.toymoban.com/news/detail-803986.html

到了这里,关于【前后端的那些事】15min快速实现图片上传,预览功能(ElementPlus+Springboot)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【前后端的那些事】开源!treeSelect树形结构数据展示

    前言 :最近写项目,发现了一些很有意思的功能,想写文章,录视频把这些内容记录下。但这些功能太零碎,如果为每个功能都单独搭建一个项目,这明显不合适。于是我想,就搭建一个项目,把那些我想将的小功能全部整合到一起。实现 搭一次环境 ,处处使用。 本文主要

    2024年02月01日
    浏览(28)
  • 【前后端的那些事】2万字详解WebRTC + 入门demo代码解析

    WebRTC是一项允许网页浏览器进行实时音视频通信的技术标准。旨在实现在浏览器之间直接进行点对点的音频、视频以及数据共享,无需安装任何插件或额外软件。 ICE全称Interactive Connectivity Establishment ,是一种交互式连接框架,他允许两个设备进行p2p通讯。 在进行p2p通讯过程

    2024年03月11日
    浏览(27)
  • 【前后端的那些事】SpringBoot 基于内存的ip访问频率限制切面(RateLimiter)

    限流就是在用户访问次数庞大时,对系统资源的一种保护手段。高峰期,用户可能对某个接口的访问频率急剧升高,后端接口通常需要进行DB操作,接口访问频率升高,DB的IO次数就显著增高,从而极大的影响整个系统的性能。如果不对用户访问频率进行限制,高频的访问容易

    2024年04月17日
    浏览(49)
  • FileReader与URL.createObjectURL实现图片、视频上传预览

    之前做图片、视频上传预览常用的方案是先把文件上传到服务器,等服务器返回文件的地址后,再把该地址字符串赋给img或video的src属性,这才实现所谓的文件预览。实际上这只是文件“上传后再预览”,这既浪费了用户的时间,也浪费了不可轻视的流量。 最近上网查资料才

    2024年02月03日
    浏览(43)
  • ant-design-vue中upload上传图片、视频实现预览功能

    有没有小伙伴在使用ant-design-vue的upload组件时,发现api文档在图片预览功能的介绍寥寥无几,而且也没提供视频预览的demo,在实际开发中碰到相应的需求直挠头~~~~,别急,下面来给大家分享一个我自己封装的upload组件,符合需求可以直接在项目中放到组件目录调用。 templat

    2024年02月12日
    浏览(34)
  • 扩展element-ui el-upload组件,实现复制粘贴上传图片文件,带图片预览功能

    控件改造 在窗口的 el-form 控件参数中添加 @paste.native 事件,事件绑定方法名 handlePaste 也可以在其他控件中添加事件监听,看个人需求。 注意: 监听粘贴事件时,需要当前页面先获取焦点,否则无法正常监听, 可以在页面加载后调用 focus() 获取焦点 粘贴功能Js部分参考资料

    2023年04月08日
    浏览(59)
  • 微信小程序---图片裁剪、旋转、预览、上传功能实现(已经封装成组件,需要的到资源下载)

    1、可以拍摄或选择本地图片上传图片数据 2、图片上传数据可以进行裁剪、选择、取消、裁剪后预览、上传以及限制大小,还可以缩放操作,需要的可以解除限制即可 1、点击图片上传按钮时,跳转页面到cropper进行图片选择剪切 wx.navigateTo({       url: `/pages/cropper/cropper?d

    2023年04月26日
    浏览(42)
  • vue实战--vue+elementUI实现多文件上传+预览(word/PDF/图片/docx/doc/xlxs/txt)

        最近在做vue2.0+element UI的项目中遇到了一个需求:需求是多个文件上传的同时实现文件的在线预览功能。需求图如下:     看到这个需求的时候,小栗脑袋一炸。并不知道该如何下手,之前的实践项目中也并没有遇到相似的功能。因此也废了一番功夫想要实现这样一个

    2024年01月23日
    浏览(51)
  • 【react + antd】antd如何自定义请求使用antd的upload组件实现图片上传且可预览可删除

    官网给出的案例无法使用封装好的请求方式上传图片,以及 无法满足上传图片后获取接口url、名称等信息的的业务需求 。这个时候需要用到customRequest这个api。 但是很遗憾,官网没有给出具体案例。 不过——博主自己试出来了( ̄︶ ̄) 要使用upload,特别重要的属性就是file

    2024年02月17日
    浏览(37)
  • el-upload实现可替换、删除、预览的图片上传。js 往返缓存(可判断当前页面是不是返回的页面)

    组件使用: UploadImage.vue 组件: BFCache是一种浏览器优化,可实现即时前进和后退载入页面。它改善了用户的浏览体验,尤其是那些网络或设备速度较慢的用户。 *我们可以通过这个方法判断当前页面是不是返回的页面* 在APP站内嵌套h5页面,判断进入拨号页返回情况: 我们需要通

    2024年01月25日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包