[推荐]SpringBoot java实现文件/附件上传下载 服务器 数据库 拿来就用,简单实用

推荐一个思路非常简单又很实用的文件上传下载方式,代码十分简练,可以开箱即用,下面是使用到的一些工具类和业务代码;

1.文件上传实现

判断文件类型的工具类,一些使用到的实体类我会凡在文末,需要可以的自取

    public static int fileType(String fileName) {
        if (fileName == null) {
            return 0;
        } else {
            // 获取文件后缀名并转化为写,用于后续比较
            String fileType = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
            // 创建图片类型数组
            String img[] = {"bmp", "jpg", "jpeg", "png", "tiff", "gif", "pcx", "tga", "exif", "fpx", "svg", "psd",
                    "cdr", "pcd", "dxf", "ufo", "eps", "ai", "raw", "wmf"};
            for (int i = 0; i < img.length; i++) {
                if (img[i].equals(fileType)) {
                    return 1;
                }
            }
            // 创建文档类型数组
            String document[] = {"txt", "doc", "docx", "xls", "htm", "html", "jsp", "rtf", "wpd", "pdf", "ppt"};
            for (int i = 0; i < document.length; i++) {
                if (document[i].equals(fileType)) {
                    return 2;
                }
            }
            // 创建视频类型数组
            String video[] = {"mp4", "avi", "mov", "wmv", "asf", "navi", "3 gp", "mkv", "f4v", "rmvb", "webm"};
            for (int i = 0; i < video.length; i++) {
                if (video[i].equals(fileType)) {
                    return 3;
                }
            }
            // 创建音乐类型数组
            String music[] = {"mp3", "wma", "wav", "mod", "ra", "cd", "md", "asf", "aac", "vqf", "ape", "mid", "ogg",
                    "m4a", "vqf"};
            for (int i = 0; i < music.length; i++) {
                if (music[i].equals(fileType)) {
                    return 4;
                }
            }
        }
        return 0;
    }

1.2 Controller层代码

部分小伙伴因为是springboot项目且版本大于2.3,@Valid注解报错找不到,需要手动添加依赖,如果对“优雅的Java参数校验”感兴趣的伙伴可以见我另一篇文章优雅的参数校验@Validated 实战 + 统一异常处理返回前端json 最全解析

        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>
@Api(tags = "附件管理")
@RestController
@RequestMapping("/attach")
public class AttachmentController {

    @Autowired
    private AttachmentService attachmentService;

    @PostMapping("/upload")
    @ApiOperation(value = "上传文件")
    public ResponseResult upload(@RequestPart("file") MultipartFile file,
                                 @RequestParam(value = "bussinessId") @Valid @NotBlank(message = "业务id不能为空") String bussinessId) throws Exception {
        return attachmentService.upload(file, bussinessId);
    }

1.3 Service层业务代码

接口类

import com.yuncheng.entity.ResponseResult;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;

public interface AttachmentService {

     ResponseResult upload(MultipartFile file, String bussinessId) throws Exception;

}

实现类,注释很清晰了


import com.yuncheng.entity.ResponseResult;
import com.yuncheng.entity.enums.AppHttpCodeEnum;
import com.yuncheng.pc.entity.UploadFile;
import com.yuncheng.pc.mapper.UploadFileMapper;
import com.yuncheng.pc.service.AttachmentService;
import com.yuncheng.utils.Tools;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class AttachmentServiceImpl implements AttachmentService {

    private static final String LOCAL_HOST = "http://10.11.0.104:8866/";

    @Autowired
    private UploadFileMapper uploadFileMapper;

    @Override
    @Transactional
    public ResponseResult upload(MultipartFile multipartFile, String bussinessId) throws Exception {
        if (multipartFile.isEmpty()) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"上传文件为空");
        }
        UploadFile uploadFile = new UploadFile();
        // 保存原文件名称,文件列表展示需要用到
        uploadFile.setFileName(multipartFile.getOriginalFilename());
        // 生成系统文件名称,不可重复,防止同名文件上传覆盖问题
        String name = RandomStringUtils.randomAlphanumeric(32) + multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".")).toLowerCase();
        uploadFile.setName(name.substring(0, name.lastIndexOf(".")));
        uploadFile.setFileSuffix(multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".")).toLowerCase());
        // 判断文件类型
        int fileType = Tools.fileType(name);
        uploadFile.setType(fileType);
        uploadFile.setDomain(LOCAL_HOST + "resource/files/");
        //这种方式比类型强转效率更高
        String pathExt = System.currentTimeMillis()+"";
        uploadFile.setPath(LOCAL_HOST + "resource/files/" + pathExt);
        uploadFile.setPathExt(pathExt);
        // 获取文件大小
        uploadFile.setSize((int)multipartFile.getSize());
        uploadFile.setBussinessId(bussinessId);
        uploadFile.setStatus(1);
        uploadFile.setCreateTime(new Date());
        uploadFile.setId(Tools.getCode32());
        uploadFileMapper.insert(uploadFile);

        // 将文件保存到本目录/resources/files/下
        // DateUtil.today()得到得是当天日期如:20230715,这个会在/resources/files/下再以日期生成一层目录
        File newFile = new File("./resources/files/"+ pathExt +  "/" + name);
        // 保证这个文件的父文件夹必须要存在
        if (!newFile.getParentFile().exists()) {
            newFile.getParentFile().mkdirs();
        }
        newFile.createNewFile();
        // 将文件内容写入到这个文件中
        InputStream is = multipartFile.getInputStream();
        FileOutputStream fos = new FileOutputStream(newFile);
        try {
            int len;
            byte[] buf = new byte[1024];
            while ((len = is.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }
        } finally {
            // 关流顺序,先打开的后关闭
            fos.close();
            is.close();
        }

        // 返回文件信息给前端
        Map resultMap = new HashMap();
        resultMap.put("id", uploadFile.getId());
        resultMap.put("path", uploadFile.getPath());
        return ResponseResult.okResult(resultMap);
    }
}

1.4 Mapper层代码

import com.yuncheng.pc.entity.UploadFile;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface UploadFileMapper {
    void insert(@Param("uploadFile") UploadFile uploadFile);
}

对应sql语句

    <insert id="insert">
        insert into "SYSDBA"."ATTACHMENT"("ID", "CREATE_BY", "CREATE_TIME", "STATUS", "UPDATE_BY",
                                          "UPDATE_TIME", "NAME", "FILE_NAME", "FILE_SUFFIX", "TYPE",
                                          "DOMAIN", "PATH", "PATH_EXT", "SIZE", "BUSSINESS_ID")
        VALUES(#{uploadFile.id},#{uploadFile.createBy},#{uploadFile.createTime},#{uploadFile.status},
               #{uploadFile.updateBy},#{uploadFile.updateTime},#{uploadFile.name},#{uploadFile.fileName},
               #{uploadFile.fileSuffix},#{uploadFile.type},#{uploadFile.domain},#{uploadFile.path},
               #{uploadFile.pathExt},#{uploadFile.size},#{uploadFile.bussinessId})
    </insert>

1.5 测试功能

至此文件上传代码就写好了,我们去postman测试一下,记住选post类型且文件类型要选对
[推荐]SpringBoot java实现文件/附件上传下载 服务器 数据库 拿来就用,简单实用_第1张图片
点击发送即上传成功
[推荐]SpringBoot java实现文件/附件上传下载 服务器 数据库 拿来就用,简单实用_第2张图片

2 文件下载代码

2.1 controller层代码

    @GetMapping("/download")
    @ApiOperation(value = "下载文件")
    public void download(HttpServletResponse response,
                                    @Valid @NotBlank(message = "id不能为空") String id) throws Exception {
         attachmentService.download(response, id);
    }

2.2 Service层代码

接口


ResponseResult download(HttpServletResponse response, String id) throws Exception;

实现类

    @Override
    public ResponseResult download(HttpServletResponse response, String id) throws Exception {
        List<UploadFile> uploadFiles = uploadFileMapper.selectOne(id, null, 1);
        if (CollectionUtils.isEmpty(uploadFiles)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "查询文件为空");
        }

        // 去./resources/files/目录下取出文件
        File downloadFile = new File("./resources/files/" +uploadFiles.get(0).getPathExt() +  "/" + uploadFiles.get(0).getName() + uploadFiles.get(0).getFileSuffix());
        if (!downloadFile.exists() || downloadFile.length() == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "查询文件为空");
        }

        InputStream is = null;
        OutputStream os = null;
        try {
            // 判断是否是图片,如果是图片加上 response.setContentType("image/jpeg"),这样就可以直接在浏览器打开而不是下载
            if (uploadFiles.get(0).getType() == 1) {
                response.setContentType("image/jpeg");
            }
            response.addHeader("Content-Length", "" + downloadFile.length());
            is = new FileInputStream(downloadFile);
            os = response.getOutputStream();
            IOUtils.copy(is, os);
        } catch (Exception e) {
            log.error("下载图片发生异常", e);
        } finally {
            try {
                if (os != null) {
                    os.flush();
                    os.close();
                }
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                log.error("关闭流发生异常", e);
            }
        }
        return ResponseResult.okResult("下载成功");
    }

2.3 Mapper层代码

    List<UploadFile> selectOne(@Param("id") String id, @Param("bussinessId") String bussinessId, @Param("status") Integer status);

对应SQL语句

    <select id="selectOne" resultType="com.yuncheng.pc.entity.UploadFile">
        select *
        from "SYSDBA"."ATTACHMENT"
        where status = #{status}
          <if test="id != null and id != ''">
              and id = #{id}
          </if>
          <if test="bussinessId != null and bussinessId != ''">
              and bussiness_id = #{bussinessId}
          </if>

    </select>

2.4 测试功能

[推荐]SpringBoot java实现文件/附件上传下载 服务器 数据库 拿来就用,简单实用_第3张图片

下面是一些使用到的实体类和建表语句,有需要的小伙伴可以自取,如果觉得文章对您有用的话记得点赞 收藏 关注哦!!!,主页全是实用文章待你来取!

文件上传实体类

import lombok.Data;

import java.util.Date;

@Data
public class UploadFile {

    private String id;

    //文件在系统中的名称
    private String name;

    //文件名称
    private String fileName;

    //文件后缀
    private String fileSuffix;

    //文件类型
    private Integer type;

    //主目录
    private String domain;

    //完整目录
    private String path;
    
    //扩展目录
    private String pathExt;

    //文件大小
    private Integer size;

    //关联的业务id
    private String bussinessId;

    //逻辑删除状态
    private Integer status;

    private Date createTime;

    private String createBy;

    private Date updateTime;

    private String updateBy;
}

统一结果返回bean

import com.yuncheng.entity.enums.AppHttpCodeEnum;
import lombok.Data;

import java.io.Serializable;

/**
 * 通用的结果返回类
 * @param 
 */
@Data
public class ResponseResult<T> implements Serializable {

    private String token;

    private Integer code;

    private String message;

    private T data;

    private String status;

    public ResponseResult() {
        this.code = 200;
    }

    public ResponseResult(Integer code, T data) {
        this.code = code;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg, T data) {
        this.code = code;
        this.message = msg;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg) {
        this.code = code;
        this.message = msg;
    }

    public static ResponseResult okResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.ok(code, null, msg);
    }

    public static ResponseResult okResult(int code, Object data,String msg) {
        ResponseResult result = new ResponseResult();
        return result.ok(code, data, msg);
    }

    public static ResponseResult okResult(Object data) {
        ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getMessage());
        if(data!=null) {
            result.setData(data);
        }
        return result;
    }

    public static ResponseResult errorResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.error(code, msg);
    }

    public static ResponseResult errorResult(AppHttpCodeEnum enums){
        return setAppHttpCodeEnum(enums,enums.getMessage());
    }

    public static ResponseResult errorResult(AppHttpCodeEnum enums, String message){
        return setAppHttpCodeEnum(enums,message);
    }

    public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){
        return okResult(enums.getCode(),enums.getMessage());
    }

    private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String message){
        return okResult(enums.getCode(),message);
    }

    public ResponseResult<?> error(Integer code, String msg) {
        this.code = code;
        this.message = msg;
        return this;
    }
    public ResponseResult<?> ok(Integer code, T data) {
        this.code = code;
        this.data = data;
        return this;
    }
    public ResponseResult<?> ok(Integer code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.message = msg;
        return this;
    }
    public ResponseResult<?> ok(T data) {
        this.data = data;
        return this;
    }
}

状态码枚举类

package com.yuncheng.entity.enums;

public enum AppHttpCodeEnum {

    // 成功段固定为200
    SUCCESS(200,"操作成功"),

    // 登录段1~50
    NEED_LOGIN(1,"需要登录后操作"),
    LOGIN_PASSWORD_ERROR(2,"密码错误"),

    // TOKEN50~100
    TOKEN_INVALID(50,"无效的TOKEN"),
    TOKEN_EXPIRE(51,"TOKEN已过期"),
    TOKEN_REQUIRE(52,"TOKEN是必须的"),

    // SIGN验签 100~120
    SIGN_INVALID(100,"无效的SIGN"),
    SIG_TIMEOUT(101,"SIGN已过期"),

    // 参数错误 500~1000
    PARAM_REQUIRE(500,"缺少参数"),
    PARAM_INVALID(501,"无效参数"),
    PARAM_IMAGE_FORMAT_ERROR(502,"图片格式有误"),
    SERVER_ERROR(503,"服务器内部错误"),

    // 数据错误 1000~2000
    DATA_EXIST(1000,"数据已经存在"),
    AP_USER_DATA_NOT_EXIST(1001,"ApUser数据不存在"),
    DATA_NOT_EXIST(1002,"数据不存在"),
    DATA_DUPLICATE(1003,"数据重复"),
    OPERATION_FAILED(1004,"操作失败"),

    // 数据错误 3000~3500
    NO_OPERATOR_AUTH(3000,"无权限操作"),
    NEED_ADMIND(3001,"需要管理员权限");

    int code;
    String message;

    AppHttpCodeEnum(int code, String errorMessage){
        this.code = code;
        this.message = errorMessage;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

建表语句

CREATE TABLE "SYSDBA"."ATTACHMENT"
(
"ID" VARCHAR2(64) NOT NULL,
"CREATE_BY" VARCHAR(64),
"CREATE_TIME" TIMESTAMP(0),
"STATUS" INTEGER,
"UPDATE_BY" VARCHAR(64),
"UPDATE_TIME" TIMESTAMP(0),
"NAME" VARCHAR(100),
"FILE_NAME" VARCHAR(100),
"FILE_SUFFIX" VARCHAR(50),
"TYPE" INTEGER,
"DOMAIN" CHARACTER(100),
"PATH" VARCHAR(100),
"PATH_EXT" VARCHAR(50),
"SIZE" INTEGER,
"BUSSINESS_ID" VARCHAR(100) NOT NULL) STORAGE(ON "MAIN", CLUSTERBTR) ;

COMMENT ON TABLE "SYSDBA"."ATTACHMENT" IS '附件表';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."BUSSINESS_ID" IS '业务id';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."CREATE_BY" IS '创建人';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."CREATE_TIME" IS '创建日期';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."DOMAIN" IS '主目录';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."FILE_NAME" IS '原文件名';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."FILE_SUFFIX" IS '文件后缀';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."ID" IS '业务id';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."NAME" IS '系统文件名';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."PATH" IS '路径';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."PATH_EXT" IS '扩展目录';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."SIZE" IS '文件大小';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."STATUS" IS '删除标志';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."TYPE" IS '类型';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."UPDATE_BY" IS '更新人';
COMMENT ON COLUMN "SYSDBA"."ATTACHMENT"."UPDATE_TIME" IS '修改时间';

你可能感兴趣的:(【Java工具常用类】,java,spring,boot,服务器)