SpringBoot上传文件实现

前言

上传文件需求也是平常开发必不可少的操做,今天就稍微总结下,通常若是是上传图片操做,不少稍微大点的公司都有专门的图片服务器可直接将图片上传至那边便可,若是没有图片服务器的话,那么此处把图片也一并归为文件进行讲解。本文代码以springBoot为准前端

 

上传到哪?

这个问题想必咱们在实现需求时也一定会思考,那么若是能肯定该项目是一个单服务器结构,那为了方便起见,可采用上传至本地服务器的项目中,若是是分布式环境而且有些文件还挺大,这里建议使用mongo的子模块GridFS实现。下面就这两种上传剖出代码java

一、上传到本地服务器spring

@RequestMapping(value = "/application/file/upload", method = RequestMethod.POST)
    public Object uoloadFile(@RequestParam("file") MultipartFile file) {
        return buildMessage(ResultModel.SUCCESS, appService.uoloadFile(file));
    }

controller层主要以MultipartFile接收便可,这里返回给前端的该文件保存后的相对路径,下面看下service层upload的具体实现:mongodb

public String uploadFile(MultipartFile file) throws NotifyException {
        // 首先校验图片格式
        List<String> imageType = Lists.newArrayList("jpg","jpeg", "png", "bmp", "gif");
        // 获取文件名,带后缀
        String originalFilename = file.getOriginalFilename();
        // 获取文件的后缀格式
        String fileSuffix = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
        if (imageType.contains(fileSuffix)) {
            // 只有当知足图片格式时才进来,从新赋图片名,防止出现名称重复的状况
            String newFileName = UUIDTypeHandler.createUUID() + originalFilename;
            // 该方法返回的为当前项目的工做目录,即在哪一个地方启动的java线程
            String dirPath = System.getProperty("user.dir");
            String path = File.separator + "uploadImg" + File.separator + newFileName;
            File destFile = new File(dirPath + path);
            if (!destFile.getParentFile().exists()) {
                destFile.getParentFile().mkdirs();
            }
            try {
                file.transferTo(destFile);
                // 将相对路径返回给前端
                return path;
            } catch (IOException e) {
                log.error("upload pic error");
                return null;
            }
        } else {
            // 非法文件
            log.error("the picture's suffix is illegal");
            throw new NotifyException(ExceptionConstants.FILE_UPLOAD_ERROR);
        }
    }

上述代码是以上传图片为例,上传文件同理,只要去掉图片格式验证便可数据库

二、上传到MongoDBspringboot

这里采用它的子模块GridFS实现,对应到代码中则是采用GridFsTemplate类来实现,GridFS使用两个集合(collection)存储文件。一个集合是chunks, 用于存储文件内容的二进制数据;一个集合是files,用于存储文件的元数据及扩展数据。当把一个文件存储到GridFS时,若是文件大于chunksize (每一个chunk块大小为256KB),会先将文件按照chunk的大小分割成多个chunk块,最终将chunk块的信息存储在fs.chunks集合的多个文档中。而后将文件信息存储在fs.files集合的惟一一份文档中。其中fs.chunks集合中多个文档中的files_id字段对应fs.files集中文档”_id”字段。服务器

读文件时,先根据查询条件在files集合中找到对应的文档,同时获得“_id”字段,再根据“_id”在chunks集合中查询全部“files_id”等于“_id”的文档。最后根据“n”字段顺序读取chunk的“data”字段数据,还原文件。app

整合MongoDB:socket

为了使本文更全面点,那么先讲springBoot如何整合mongo,因为springBoot默认是没有提供配置链接池的属性,即你在application.yaml中的链接配置是不带链接池功能,所以这里我建议采用代码方式进行配置mongo,同时代码中配置能更好的切换不一样的数据库以建立不一样的MongoDbFactory,先贴pom文件依赖:分布式

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-mongodb</artifactId>
		</dependency>

而后看下代码配置mongodb与springboot整合:

@Service
public class MongoConfig extends AbstractMongoConfiguration {

    @Override
    public MongoClient mongoClient() {
        MongoClient mongoClient = getMongoClient();
        return mongoClient;
    }

    public MongoClient getMongoClient() {
        // MongoDB地址列表
        List<ServerAddress> serverAddresses = new ArrayList<>();
        serverAddresses.add(new ServerAddress("xxxx:27017"));
        // 链接认证
        MongoCredential credential = MongoCredential.createCredential("xxx", "xx", "xxx".toCharArray());
        MongoClientOptions.Builder builder = MongoClientOptions.builder();

        //最大链接数
        builder.connectionsPerHost(10);
        //最小链接数
        builder.minConnectionsPerHost(0);
        //超时时间
        builder.connectTimeout(1000*3);
        // 一个线程成功获取到一个可用数据库以前的最大等待时间
        builder.maxWaitTime(5000);
        //此参数跟connectionsPerHost的伺机为一个线程变为可用的最大阻塞数,超过此伺机数以后的全部线程将及时获取一个异常.eg.connectionsPerHost=10 and threadsAllowedToBlockForConnectionMultiplier=5,最多50个线程等级一个连接,推荐配置为5
        builder.threadsAllowedToBlockForConnectionMultiplier(5);
        //最大空闲时间
        builder.maxConnectionIdleTime(1000*10);
        //设置池链接的最大生命时间。
        builder.maxConnectionLifeTime(1000*10);
        //链接超时时间
        builder.socketTimeout(1000*10);

        MongoClientOptions myOptions = builder.build();
        MongoClient mongoClient = new MongoClient(serverAddresses, credential, myOptions);
        return mongoClient;
    }

    @Override
    protected String getDatabaseName() {
        return "xxxx";
    }

    /**
     * 获取另外一个数据库
     * @return
     */
    public String getFilesDataBaseName() {
        return "xxx";
    }

    /**
     * 用于切换不一样的数据库
     * @return
     */
    public MongoDbFactory getDbFactory(String dataBaseName) {
        MongoDbFactory dbFactory = null;
        try {
            dbFactory = new SimpleMongoDbFactory(getMongoClient(), dataBaseName);
        } catch (Exception e) {
            log.error("Get mongo client have an error, please check reason...", e.getMessage());
        }
        return dbFactory;
    }

    /**
     * 获取文件存储模块
     * @return
     */
    public GridFsTemplate getGridFS() {
        return new GridFsTemplate(getDbFactory(getFilesDataBaseName()), mongoTemplate.getConverter());
    }

}

为了测试方便,各参数都直接写死了,建议写到配置文件中去(例如disconf),方便更改或扩展,这里须要注意的是继承的getDatabaseName()方法中返回的数据库为mongoTemplate默认使用的库,若需切换到第二个库,请看以下代码

public String upload(MultipartFile file) {
        // 拿到本文的重点类
        GridFsTemplate gridFsTemplate = mongoConfig.getGridFS();
        try {
            InputStream in = file.getInputStream();
            String filename = file.getOriginalFilename();
            String contentType = file.getContentType();
            // 这是扩展属性,供不一样的业务需求,往后可根据特意的参数进行查询
            BasicDBObject dbObject = new BasicDBObject("name", "xj");
            dbObject.put("key", "value");
            String fileId = gridFsTemplate.store(in, filename, contentType, dbObject).toString();
            return fileId;
        } catch (IOException e) {
            log.error("transfer in error .");
            return null;
        }
    }

重点即为图上的store()方法,有多个重载方法,可自行选择。

到这里其实还未结束,springboot上传文件默认支持的大小为1mb,所以当你超过这个限制是会报以下错:

修改文件上传的大小便可,在yaml文件下增长以下配置:

spring:
  servlet:
    multipart:
      #单个数据的大小
      max-file-size: 100MB
      #总数据的大小
      max-request-size: 100MB