spring boot开发mongoDB做文档型数据库存储文档

首先这盘博客是参考SpringBoot Mongodb文件存储服务器,并且修改了原来博客中过时的废弃的方法,并把我实际做的公布出来。

一、首先提醒

因为我做的是多模块项目,用dubbozk实现的微服务,但是因为上传的文件过大,所以肯定不能通过微服务传输数据再到服务的实现类里去上传文件,所以我就在webapp(应该都知道是哪个模块)这个模块直接和mongoDB连接传输文件。

二、依赖(都是webapp模块下的)

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

application.properties

spring.data.mongodb.uri=mongodb://user:123456@127.0.0.1:27017/first?authSource=admin&authMechanism=SCRAM-SHA-256
spring.servlet.multipart.max-file-size=30MB
spring.servlet.multipart.max-request-size=50MB

三、用到的POJO

@Data
public class FileDocument implements Serializable {
    /**
     * id
     */
    private String id;
    /**
     * 文件名称
     */
    private String name;
    /**
     * 文件大小
     */
    private long size;
    /**
     * 上传时间
     */
    private Date uploadDate;
    /**
     * 文件的md5值
     */
    private String md5;
    /**
     * 文件内容
     */
    private byte[] content;
    /**
     * 文件类型
     */
    private String contentType;
    /**
     * 文件后缀
     */
    private String suffix;
    /**
     * 文件描述
     */
    private String description;
    /**
     * 大文件管理GridFS的ID
     */
    private String gridfsId;
}

四、服务和实现类

import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.Optional;

/**
 * @author 孔超
 * @date 2020/5/11 15:20
 */
public interface FileService {
    /**
     * 保存信息的文件
     *
     * @param md5
     * @param file
     * @return
     */
    public FileDocument saveFile(String md5, MultipartFile file);

    /**
     * 移除文件
     *
     * @param id
     * @param isDeleteFile
     */
    public void removeFile(String id, boolean isDeleteFile);

    /**
     * 根据id查询附件
     *
     * @param id
     * @return
     */
    public Optional<FileDocument> getById(String id);

    /**
     * 根据md5获取文件对象
     *
     * @param md5
     * @return
     */
    public FileDocument getByMd5(String md5);

    /**
     * 文件分页
     *
     * @param pageIndex
     * @param pageSize
     * @return
     */
    public List<FileDocument> listFilesByPage(int pageIndex, int pageSize);
}

import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.mongodb.client.result.DeleteResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Field;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

/**
 * @author 孔超
 * @date 2020/5/11 15:18
 */
@Slf4j
@Service("fileService")
public class FileServiceImpl implements FileService {
    private static String collectionName = "fileDate";

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private GridFsTemplate gridFsTemplate;
    @Autowired
    private GridFSBucket gridFSBucket;

    /**
     * 表单上传附件
     *
     * @param md5
     * @param file
     * @return
     */
    @Override
    public FileDocument saveFile(String md5, MultipartFile file) {
        //已存在该文件,则实现秒传
        FileDocument fileDocument = getByMd5(md5);
        if (fileDocument != null) {
            return fileDocument;
        }

        fileDocument = new FileDocument();
        fileDocument.setName(file.getOriginalFilename());
        fileDocument.setSize(file.getSize());
        fileDocument.setContentType(file.getContentType());
        fileDocument.setUploadDate(new Date());
        fileDocument.setMd5(md5);
        String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
        fileDocument.setSuffix(suffix);

        try {
            //文件存入gridfs
            String gridfsId = uploadFileToGridFS(file.getInputStream(), file.getContentType());
            fileDocument.setGridfsId(gridfsId);
            //上传的信息保存在mongodb
            fileDocument = mongoTemplate.save(fileDocument, collectionName);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return fileDocument;
    }

    /**
     * 上传文件到Mongodb的GridFs中
     *
     * @param in
     * @param contentType
     * @return
     */
    private String uploadFileToGridFS(InputStream in, String contentType) {
        String gridfsId = UUID.randomUUID().toString();
        //文件,存储在GridFS中
        gridFsTemplate.store(in, gridfsId, contentType);
        return gridfsId;
    }

    /**
     * 删除附件
     *
     * @param id           文件id
     * @param isDeleteFile 是否删除文件
     */
    @Override
    public void removeFile(String id, boolean isDeleteFile) {
        FileDocument fileDocument = mongoTemplate.findById(id, FileDocument.class, collectionName);
        if (fileDocument != null) {
            Query query = new Query().addCriteria(Criteria.where("_id").is(id));
            DeleteResult result = mongoTemplate.remove(query, collectionName);
            System.out.println("result:" + result.getDeletedCount());

            if (isDeleteFile) {
                Query deleteQuery = new Query().addCriteria(Criteria.where("filename").is(fileDocument.getGridfsId()));
                gridFsTemplate.delete(deleteQuery);
            }
        }
    }

    /**
     * 查询附件
     *
     * @param id 文件id
     * @return
     * @throws IOException
     */
    @Override
    public Optional<FileDocument> getById(String id) {
        FileDocument fileDocument = mongoTemplate.findById(id, FileDocument.class, collectionName);
        if (fileDocument != null) {
            Query gridQuery = new Query().addCriteria(Criteria.where("filename").is(fileDocument.getGridfsId()));
            try {
                GridFSFile fsFile = gridFsTemplate.findOne(gridQuery);
                GridFSDownloadStream in = gridFSBucket.openDownloadStream(fsFile.getObjectId());
                if (in.getGridFSFile().getLength() > 0) {
                    GridFsResource resource = new GridFsResource(fsFile, in);
                    fileDocument.setContent(inputStreamToByte(resource.getInputStream()));
                    return Optional.of(fileDocument);
                } else {
                    fileDocument = null;
                    return Optional.empty();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return Optional.empty();
    }

    /**
     * 流转换成byte数组
     *
     * @param inStream
     * @return
     * @throws IOException
     */
    private byte[] inputStreamToByte(InputStream inStream) throws IOException {
        ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
        //buff用于存放循环读取的临时数据
        byte[] buff = new byte[100];
        int rc = 0;
        while ((rc = inStream.read(buff, 0, 100)) > 0) {
            swapStream.write(buff, 0, rc);
        }
        return swapStream.toByteArray();
    }


    /**
     * 根据md5获取文件对象
     *
     * @param md5
     * @return
     */
    @Override
    public FileDocument getByMd5(String md5) {
        Query query = new Query().addCriteria(Criteria.where("md5").is(md5));
        return mongoTemplate.findOne(query, FileDocument.class, collectionName);
    }

    /**
     * 文件分页
     *
     * @param pageIndex
     * @param pageSize
     * @return
     */
    @Override
    public List<FileDocument> listFilesByPage(int pageIndex, int pageSize) {
        Sort sort = Sort.by(Sort.Order.desc("uploadDate"));
        Query query = new Query().with(sort);
        long skip = (pageIndex - 1) * pageSize;
        query.skip(skip);
        query.limit(pageSize);
        Field field = query.fields();
        field.exclude("content");
        List<FileDocument> files = mongoTemplate.find(query, FileDocument.class, collectionName);
        return files;
    }
}

五、FileController

1.返回值Result是我自己的工具类,用于返回信息的,你可以自己编
2.这里面和上传实现类是有重复的,我没整理,应该整理成工具类的
3.下面在线预览的前端需要进行处理,这里的简单例子不支持

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Optional;

/**
 * @author 孔超
 * @date 2020/5/11 15:17
 */
@Slf4j
@RestController
@RequestMapping("files")
public class FileController {
    @Resource
    FileService fileService;

    /**
     * 上传文件列表
     *
     * @param pageIndex
     * @param pageSize
     * @return
     */
    @RequestMapping("/list")
    public List<FileDocument> list(int pageIndex, int pageSize) {
        return fileService.listFilesByPage(pageIndex, pageSize);
    }

    /**
     * 在线显示文件:目前不能实现在线预览,需要前端进行处理,这个简单例子是不行
     *
     * @param id 文件id
     * @return
     */
    @GetMapping("/view")
    public ResponseEntity<Object> serveFileOnline(String id) {
        Optional<FileDocument> file = fileService.getById(id);
        if (file.isPresent()) {
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "fileName=" + file.get().getName())
                    .header(HttpHeaders.CONTENT_TYPE, file.get().getContentType())
                    .header(HttpHeaders.CONTENT_LENGTH, file.get().getSize() + "").header("Connection", "close")
                    .body(file.get().getContent());
        } else {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File was not found");
        }
    }

    /**
     * 下载文件
     *
     * @param id
     * @return
     * @throws UnsupportedEncodingException
     */
    @GetMapping("/down")
    public ResponseEntity<Object> downloadFileById(String id) throws UnsupportedEncodingException {
        Optional<FileDocument> file = fileService.getById(id);
        if (file.isPresent()) {
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; fileName=" + URLEncoder.encode(file.get().getName(), "utf-8"))
                    .header(HttpHeaders.CONTENT_TYPE, "application/octet-stream")
                    .header(HttpHeaders.CONTENT_LENGTH, file.get().getSize() + "").header("Connection", "close")
                    .body(file.get().getContent());
        } else {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body("File was not found");
        }
    }

    /**
     * 表单上传文件
     * 当数据库中存在该md5值时,可以实现秒传功能
     *
     * @param file 文件
     * @return
     */
    @PostMapping("/upload")
    public Result formUpload(@RequestParam("file") MultipartFile file) {
        Result result = null;
        try {
            if (file != null && !file.isEmpty()) {
                String fileMd5 = getMD5(file.getInputStream());
                FileDocument fileDocument = fileService.saveFile(fileMd5, file);

                System.out.println(fileDocument);
                result = new Result(1, "上传成功", fileDocument.getId());
            } else {
                result = new Result(0, "请上传文件");
            }
        } catch (IOException e) {
            log.error("IOE异常", e);
            result = new Result(-1, "系统异常");
        } catch (Exception e) {
            log.error("上传文件系统异常,", e);
            result = new Result(-1, "系统异常");

        }
        return result;
    }

    /**
     * 删除附件
     *
     * @param id
     * @return
     */
    @GetMapping("/delete")
    public Result deleteFileByGetMethod(String id) {
        Result result = null;
        if (id != null) {
            fileService.removeFile(id, true);
            result = new Result(1, "删除成功");
        } else {
            result = new Result(0, "请传输文件");
        }
        return result;
    }

    /**
     * 流转换成MD5字符串
     *
     * @param inputStream
     * @return
     */
    private String getMD5(InputStream inputStream) {
        BigInteger md5 = null;
        try {
            byte[] buffer = inputStreamToByte(inputStream);
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(buffer, 0, buffer.length);
            byte[] b = md.digest();
            md5 = new BigInteger(1, b);
        } catch (NoSuchAlgorithmException | IOException e) {
            e.printStackTrace();
        }
        assert md5 != null;
        return md5.toString(16);
    }

    /**
     * 流转换成byte数组
     *
     * @param inStream
     * @return
     * @throws IOException
     */
    private byte[] inputStreamToByte(InputStream inStream) throws IOException {
        ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
        //buff用于存放循环读取的临时数据
        byte[] buff = new byte[100];
        int rc = 0;
        while ((rc = inStream.read(buff, 0, 100)) > 0) {
            swapStream.write(buff, 0, rc);
        }
        return swapStream.toByteArray();
    }

}

六 简单的例子

单独的Controller

这个和上面的FileController是不一样的,当然也可以在一起

 @RequestMapping("/gotoTest.do")
    public String gotoTest(Model model) {
    	//这里我定死了,没有根据前端提供的分页数据的来,这里的list是mongoDB里的数据的信息,
    	//如果想根据前端提供的数据来的话上面的参数加上@RequestParam(value = "pageIndex" , defaultValue = "1") int pageIndex,@RequestParam(value = "pageSize" , defaultValue = "10") int pageSize
        List<FileDocument> fileDocumentList = fileService.listFilesByPage(1, 10);
        model.addAttribute("fileDocumentList", fileDocumentList);
        return "text";
    }

    @RequestMapping("/upload")
    public String upload() {
        return "upload";
    }

test.ftl

我用的freemarker

 <div class="find-top1">
        <form action="" id="form_Recruitment">
            <table class="top-table">
                <tr>
                    <td>id</td>
                    <td>名字</td>
                    <td>大小</td>
                    <td>操作</td>
                </tr>
                <#list fileDocumentList as fileDocument>
                    <tr>
                        <td>${fileDocument.id}</td>
                        <td>${fileDocument.name}</td>
                        <td>${fileDocument.size}</td>
                        <td><a href="/files/view?id=${fileDocument.id}">预览</a><a>下载</a> <a>删除</a></td>
                    </tr>
                </#list>

            </table>
        </form>
    </div>

效果图

在这里插入图片描述

upload.ftl

<form method="POST" enctype="multipart/form-data" action="/files/upload">
    <p>
        文件:<input type="file" name="file"/>
    </p>
    <p>
        <input type="submit" value="上传"/>
    </p>
</form>

效果图

在这里插入图片描述
上面就是特别简单的实例

七 实际的例子

文档数据库就当好它的数据库,它记录的是文档,一般我们创建时很多时候是很多input和文件一起提交到后端,而关系型数据库记录的是文档在文档数据库中的id或者唯一标识符。
主要在于怎么提交既有文件又有input的form表单

前端:

<form name="form_recruiters">
            <table class="top-table">
                <tr>
                    <td class="top-table-label">姓名:</td>
                    <td><input type="text" name="name"></td>
                    <td class="top-table-label">应聘岗位:</td>
                    <td><input type="text" name="positions"></td>
                    <td class="top-table-label">工作类型:</td>
                    <td>
                        <select name="type">
                            <option>实习</option>
                            <option>全职</option>
                        </select>
                    </td>
                </tr>
                <tr>
                    <td class="top-table-label">上传简历</td>
                    <td colspan="5"><input type="file" name="file"></td>
                </tr>
                <tr>
                    <td colspan="6" style="text-align: center">
                        <i class="glyphicon glyphicon-edit"></i><input type="button" value="提交" data-dismiss="modal" onclick="submitRecruiters()"/>
                    </td>
                </tr>
            </table>
        </form>

js

   function submitRecruiters() {
   		//创建一个formdate对象传输
        var form = document.forms.namedItem("form_recruiters");
        var formData = new FormData(form);
        $.ajax({
            url: "/saveRecruiters.do",
            type: "POST",
            data: formData,
            dataType: "json",
            contentType: false, //必须
            processData: false, //必须
            cache: false,
            success: function (result) {
                if (result.code === 1) {
                    location.reload();
                }
                alert(result.msg);
            },
            error: function () {
                alert("连接服务器异常,请刷新后重试")
            }
        });
    }

后端

 @PostMapping("/saveRecruiters.do")
    @ResponseBody
    public Result saveRecruiters(@RequestParam("file") MultipartFile file, @Valid Recruiters recruiters) {
        if (file == null || file.isEmpty()) {
            return new Result(0, "请上传简历");
        }
        //uploadFile这个就是FileController中的上传文件的代码,返回的是文档数据库中的id
        String resumeId = uploadFile(file);
        if (resumeId == null) {
            return new Result(0, "简历上传失败,请重新上传");
        }
        recruiters.setResumeId(resumeId);
        Result result = null;
        try {
            Long isSuc = recruitersService.insert(recruiters);
            if (isSuc != 1) {
                result = new Result(0, "添加失败,请重新添加");
            } else {
                result = new Result(1, "添加成功");
            }
        } catch (Exception e) {
			 log.error("保存应聘者信息出现系统异常recruiters{}", JSON.toJSONString(recruiters), e);
            result = new Result(1,"出现系统异常");
        }
        return result;
    }

这样就完成了

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值