SpringBoot之文件上传实例

今天给大家上道硬菜,坐等有缘人发现,也希望能帮助到有这这需求方面的老哥。

我们在做项目的时候,有一些文件上传的需求,比如个人简历上的大头照,某些调查报告,pdf文档,还有小视屏啥之类的,这些通通都是文件,用户在上传文件的时候,必须用到文件上传功能,那么这个文件就存放到我们服务器对应的文件夹下,在数据库生成一条文件存放地址,这样下次求可以通过域名ip获取到我们存放的文件,废话不多说,我们开始实现。

先来把框架搭起来:

环境要求

Java1.8及以上

本文采用Java 1.8.0_73 . Spring Boot 2.0 + TKmybatis

 

 

 

 导入项目maven依赖

    <dependencies>

        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.13 </version>
        </dependency>

        <!--阿里json包-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.41</version>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
        <!--web起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--alibaba easyexcel-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.6</version>
        </dependency>

        <dependency>
            <groupId>org.lionsoul</groupId>
            <artifactId>ip2region</artifactId>
            <version>1.7</version>
        </dependency>


        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/tk.mybatis/mapper -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper</artifactId>
            <version>4.0.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/tk.mybatis/mapper-spring-boot-starter -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.0.3</version>
        </dependency>

        <!--springboot起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--mysql连接依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
            <version>5.1.46</version>
        </dependency>
        <!--spring-boot测试工具起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

项目结构:

 application.yml文件配置

server:
  port: 8888
  servlet:
    # 上下文路径
    context-path: /api
  tomcat:
    uri-encoding: UTF-8

pagehelper:
  helperDialect: mysql
  reasonable: true
  supportMethodsArguments: true
  pageSizeZero: false #pageSize=0

spring:
  application:
    #  项目名称
    name: zigao
  profiles:
    # 环境
    active: dev
  jackson:
    # json 序列化配置
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT
  http:
    #  编码
    encoding:
      charset: UTF-8
      enabled: true
      force: true
  #mvc静态资源访问路径
  mvc:
    static-path-pattern: /resources/**
  resources:
    static-locations: file:${file.uploadFileDir}

application-dev.yml文件配置

spring:
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test3?useUnicode=true&characterEncoding=utf-8
    username: root
    password: 123456
  #设置文件大小
  servlet:
    multipart:
      max-file-size: 50MB
      max-request-size: 50MB
mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  type-aliases-package: com.zigao
  configuration:
    call-setters-on-nulls: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

file:
  uploadFileDir: D:\zigaoProject
  uploadTempDir: D:\zigaoProject
  uploadContent: /file
/**
 * 启动类
 *
 * @author zigao
 * @date 2020年08月01日 11:46
 */

@SpringBootApplication
@MapperScan("com.zigao.com.dao")
public class zigaoApplication {
    public static void main(String[] args) {
        SpringApplication.run(zigaoApplication.class, args);
        System.out.println("===============================已启动================================");
    }
}

注意 :注意 : 注意  这里要特别注意:这里一定要导入tk下的包,一定要导入tk下的包,一定要导入tk下的包,重要的事情说三遍

 不然就会报如下错:

 还有注意的是:使用TKmybatis一定要使用@MapperScan注解,并一定要把包扫描到dao层

 还有注意的是:使用TKmybatis一定要使用@MapperScan注解,并一定要把包扫描到dao层

 还有注意的是:使用TKmybatis一定要使用@MapperScan注解,并一定要把包扫描到dao层

启动成功,框架算是搭建好了。

定义数据统一返回格式:

/**
 * 
 *
 * @author zigao
 * @date 2020年08月01日 11:48
 */
public class ResponseMessage implements Serializable {
    int status;
    Object message;
    String errmsg;

    public int getStatus() {
        return this.status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public Object getMessage() {
        return this.message;
    }

    public void setMessage(Object message) {
        this.message = message;
    }

    public String getErrmsg() {
        return this.errmsg;
    }

    public void setErrmsg(String errmsg) {
        this.errmsg = errmsg;
    }

    public ResponseMessage() {
    }

    public ResponseMessage(int status) {
        this.status = 0;
    }

    public ResponseMessage(Object data) {
        this.status = 0;
        this.message = data;
    }

    public ResponseMessage(int status, Object data) {
        this.status = status;
        this.message = data;
    }

    public ResponseMessage(int status, String err) {
        this.status = status;
        this.errmsg = err;
    }
}

 我们把表设计一下:这个表主要用来存储文件上传的一些信息比如,文件编码,录入单位,录入人,文件名称,文件类型等等。

 我们一个个解释下:

 文件编号 :  指定这个上传的文件唯一性,必须要唯一。

 文件名称:这个文件叫啥  比如哪个美女的靓照  

 表名:这个可以理解,这条信息对应哪张业务表

 对应表主键: 就是这条信息对应哪张业务表的主键  

文件格式:比如这个文件是小电影,那他就是视屏格式,如果是文档 就是文档格式

文件类型:主要根据业务描述类型信息

文件路径:用来存储文件所在服务器的路径信息

文件说明:比如这个小电影看起来很nice

文件时间:这个文件什么时候上传进来的

录入人:就是谁上传的这个文件

录入单位:哪个单位负责录入这个文件的

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for file_infos
-- ----------------------------
DROP TABLE IF EXISTS `file_infos`;
CREATE TABLE `file_infos`  (
  `FILE_CODE` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '文件编号',
  `FILE_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件名称',
  `TABLE_NAME` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '表名',
  `OBJECT_ID` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '对应表主键',
  `FILE_FORMAT` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件格式',
  `FILE_TYPE` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件类型',
  `FILE_URL` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件路径',
  `DESCRIPTION` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件说明',
  `FILE_TIME` datetime(0) NULL DEFAULT NULL COMMENT '文件时间',
  `USER_CODE` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '录入人',
  `FILLED_UNIT` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '录入单位',
  PRIMARY KEY (`FILE_CODE`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '文件表' ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;
@Data
public class FileInfos implements Serializable {

    private static final long serialVersionUID = 8478273528550897842L;

    /**
     * 文件编号
     */

    private String fileCode;

    /**
     * 文件名称
     */

    private String fileName;

    /**
     * 表名
     */

    private String tableName;

    /**
     * 对应表主键
     */

    private String objectId;

    /**
     * 文件格式
     */
    private String fileFormat;

    /**
     * 文件类型
     */
    private String fileType;

    /**
     * 文件路径
     */
    private String fileUrl;

    /**
     * 文件说明
     */
    private String description;

    /**
     * 文件时间
     */
    private Timestamp fileTime;

    /**
     * 录入人
     */
    private String userCode;

    /**
     * 录入单位
     */
    private String filledUnit;

}

  文件上传工具类:

public class FileUtils {

    /**
     * 创建目录,如果目录不存在这创建目录
     *
     * @param path 目录路径
     */
    public static void mkdirs(String path) {
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    /**
     * 删除文件
     *
     * @param path
     */
    public static void deleteFile(String path) {
        File file = new File(path);
        if (file.exists()) {
            file.delete();
        }
    }

    /**
     * 创建文件
     *
     * @param stream 文件流
     * @param path   文件保存路径
     * @throws IOException
     */
    public static void createFile(InputStream stream, String path) throws IOException {
        FileOutputStream outputStream = null;
        int byteCount;
        try {
            outputStream = new FileOutputStream(path);
            byte[] bytes = new byte[1024];
            while ((byteCount = stream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, byteCount);
            }
        } finally {
            closeOutputStream(outputStream);
            closeInputStream(stream);
        }
    }

    /**
     * 关闭 InputStream 流
     *
     * @param stream InputStream 流对象
     */
    private static void closeInputStream(InputStream stream) {
        try {
            stream.close();
        } catch (IOException e) {
        }
    }

    /**
     * 关闭 OutputStream 流
     *
     * @param outputStream OutputStream 流对象
     */
    private static void closeOutputStream(OutputStream outputStream) {
        try {
            outputStream.close();
        } catch (IOException e) {
        }
    }

    /**
     * MultipartFile 转 File
     *
     * @param file
     * @throws Exception
     */
    public static File multipartFileToFile(MultipartFile file) throws Exception {

        File toFile = null;
        if (!"".equals(file) && file.getSize() > 0) {
            InputStream ins;
            ins = file.getInputStream();
            toFile = new File(file.getOriginalFilename());
            inputStreamToFile(ins, toFile);
            ins.close();
        }
        return toFile;
    }

    /**
     * 获取流文件
     */
    private static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String getPath() {
        return FileUtils.class.getResource("/").getPath();
    }
}

Controller层:

@RestController
@RequestMapping("/file")
public class FileController {
       //打印日志
       public Logger logger = LoggerFactory.getLogger(this.getClass());


       @Autowired
       private FileService fileService;

       /**
        * 上传文件
        *
        * @param file
        * @return
        */
       @PostMapping("/upload")
       public ResponseMessage upload(@RequestParam("file") MultipartFile file) {
              ResponseMessage responseMessage = new ResponseMessage(0);
              try {
                     FileInfos fileInfo = fileService.upload(file);
                     responseMessage.setMessage(fileInfo);
              } catch (Exception e) {
                     logger.error("上传文件异常,", e);
                     throw new RuntimeException(e.getMessage());
              }
              return responseMessage;
       }




}

Service层:

public interface FileService {

  FileInfos createFile(MultipartFile file);

  FileInfos upload(MultipartFile file);

  void addFiles(FileInfos fileInfo);
}
@Service
public class FileServiceImpl implements FileService {

    @Autowired
    private FileInfoMapper fileInfoMapper;



    @Value("${file.uploadFileDir}")
    private String uploadFileDir;

    @Value("${file.uploadContent}")
    private String uploadContent;



    @Override
    public FileInfos createFile(MultipartFile file) {

        //获取文件基本信息
        String originalName = file.getOriginalFilename();
        String suffix = originalName.substring(originalName.lastIndexOf("."));
        String name = generateFileName() + suffix;
        FileInfos fileInfos = new FileInfos();
        try {
            //创建文件路径
            String monthPath = getMonthPath();
            String filePath = monthPath + "/" + name;
            String dir = uploadFileDir + uploadContent + "/" + monthPath;
            String savePath = uploadContent + "/" + filePath;
            FileUtils.mkdirs(dir);
            //上传文件
            FileUtils.createFile(file.getInputStream(), dir + "\\" + name);
            //保存附件
            fileInfos.setFileName(originalName);
            fileInfos.setFileUrl(savePath);
            fileInfos.setFileTime(Timestamp.valueOf(LocalDateTime.now()));
            fileInfos.setFileFormat(suffix);
            fileInfos.setFileCode(UUID.randomUUID().toString().replaceAll("\\-", ""));
            fileInfoMapper.insert(fileInfos);
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage());
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
        return fileInfos;
    }


    @Override
    public FileInfos upload(MultipartFile file) {
        FileInfos fileInfo = this.createFile(file);
        return fileInfo;
    }



    /**
     * 生成文件名称
     *
     * @return
     */
    private String generateFileName() {
        Format format = new SimpleDateFormat("yyyyMMddHHmmss");
        return format.format(new Date()) + UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 按照月份存储
     *
     * @return
     */
    private String getMonthPath() {
        Format format = new SimpleDateFormat("yyyyMM");
        return format.format(new Date());
    }


    @Override
    public void addFiles(FileInfos fileInfo) {
        fileInfo.setFileCode(UUID.randomUUID().toString());
        fileInfoMapper.insert(fileInfo);
    }


}

dao层:

@Repository
public  interface FileInfoMapper extends Mapper<FileInfos> {

}

代码写完啦,结构如下:我们来测试下:

上传成功,并返回结果信息。

打开文件目录,在对应文件下,证明上传成功。

 OK,下面我们来测试下访问上传的图片。

 

 好,整体功能就写完了,有哪里不明白的老哥,或者哪里写的不好的地方,欢迎指点一二。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值