@Test
public void findOne() throws Exception {
// 获取文件ID
String objectId = “57fbaffcec773716ecc54ef4”;
// 创建一个容器,传入一个MongoDatabase类实例db
GridFSBucket bucket = GridFSBuckets.create(mongoDatabase());
// 获取内容
GridFSFindIterable gridFSFindIterable = bucket.find(Filters.eq(“_id”, new ObjectId(objectId)));
GridFSFile gridFSFile = gridFSFindIterable.first();
System.out.println("filename: " + gridFSFile.getFilename());
}
• 下载文件
@Test
public void download() throws Exception {
// 获取文件ID
String objectId = “57fbaffcec773716ecc54ef4”;
// 获取文件流
File file = new File(“D:/zookeeper-3.4.8.tar.gz”);
// 创建一个容器,传入一个MongoDatabase类实例db
GridFSBucket bucket = GridFSBuckets.create(mongoDatabase());
// 创建输出流
OutputStream os = new FileOutputStream(file);
// 下载
bucket.downloadToStream(new ObjectId(objectId), os);
System.out.println(“下载完成。”);
}
• 删除文件
@Test
public void delete() throws Exception {
// 获取文件ID
String objectId = “57fbaffcec773716ecc54ef4”;
// 创建一个容器,传入一个MongoDatabase类实例db
GridFSBucket bucket = GridFSBuckets.create(mongoDatabase());
// 删除
bucket.delete(new ObjectId(objectId));
System.out.println(“删除完成。”);
}

二、SpringBoot整合MongoDB存储文件

===========================================================================================

MongoDB可以将文件直接存储在文档或者通过GridFS存储大文件,这里同样进行SpringBoot整合MongoDB的两种实现。

1、MongoDB存储小文件


SpringBoot整合MongoDB将文件以文档形式直接存入集合,和普通的MongDB存储区别不大。

1.1、添加依赖

  • spring-boot-starter-data-mongodb:用来操作MongoDB
  • spring-boot-starter-thymeleaf:前端页面采用thymeleaf模板
org.springframework.boot
spring-boot-starter-data-mongodb
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web

1.2、配置

server.address=localhost

server.port=8081

thymeleaf配置,开发环境不启用缓存,正式环境下请启用缓存,提高性能

spring.thymeleaf.cache=false

thymeleaf对html元素格式要求严格,设置它的mode为HTML,忘记结束标签后不会报错

spring.thymeleaf.mode=HTML

编码

spring.http.encoding.charset=UTF-8

spring.http.encoding.enabled=true

MongoDB 配置

连接url

spring.data.mongodb.uri=mongodb://test:test@localhost:27017/filetest

文件上传限制

spring.servlet.multipart.max-file-size=30MB

spring.servlet.multipart.max-request-size=50MB

1.3、模型层

文件模型类:
/**
• @Author 三分恶
• @Date 2020/1/11
• @Description 文档类
*/
@Document
public class FileModel {
@Id // 主键
private String id;
private String name; // 文件名称
private String contentType; // 文件类型
private long size;
private Date uploadDate;
private String md5;
private Binary content; // 文件内容
private String path; // 文件路径
/**
*省略getter/setter
*/
protected FileModel() {
}
public FileModel(String name, String contentType, long size, Binary content) {
this.name = name;
this.contentType = contentType;
this.size = size;
this.content = content;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof FileModel)) return false;
FileModel fileModel = (FileModel) o;
return size == fileModel.size &&
Objects.equals(id, fileModel.id) &&
Objects.equals(name, fileModel.name) &&
Objects.equals(contentType, fileModel.contentType) &&
Objects.equals(uploadDate, fileModel.uploadDate) &&
Objects.equals(md5, fileModel.md5) &&
Objects.equals(content, fileModel.content) &&
Objects.equals(path, fileModel.path);
}
@Override
public int hashCode() {
return Objects.hash(id, name, contentType, size, uploadDate, md5, content, path);
}
}
1.4、持久层
采用MongoRepository的方式操作MongoDB,只需在接口里继承MongoRepository,就可以调用MongoRepository的内置方法。
public interface FileRepository extends MongoRepository<FileModel,String> {
}
1.5、服务层
服务层接口:
public interface FileService {
/**
• 保存文件
*/
FileModel saveFile(FileModel file);
/**
• 删除文件
*/
void removeFile(String id);
/**
• 根据id获取文件
*/
Optional getFileById(String id);
/**
• 分页查询,按上传时间降序
• @return
*/
List listFilesByPage(int pageIndex, int pageSize);
}
服务层实现类:
@Service
public class FileServiceImpl implements FileService {
@Autowired
private FileRepository fileRepository;
@Override
public FileModel saveFile(FileModel file) {
return fileRepository.save(file);
}
@Override
public void removeFile(String id) {
fileRepository.deleteById(id);
}
@Override
public Optional getFileById(String id) {
return fileRepository.findById(id);
}
@Override
public List listFilesByPage(int pageIndex, int pageSize) {
Page page = null;
List list = null;
Sort sort = Sort.by(Sort.Direction.DESC,“uploadDate”);
Pageable pageable = PageRequest.of(pageIndex, pageSize, sort);
page = fileRepository.findAll(pageable);
list = page.getContent();
return list;
}
}
1.6、控制层
@Controller
public class FileController {
@Autowired
private FileService fileService;
@Value(“${server.address}”)
private String serverAddress;
@Value(“${server.port}”)
private String serverPort;
@RequestMapping(value = “/”)
public String index(Model model) {
// 展示最新二十条数据
model.addAttribute(“files”, fileService.listFilesByPage(0, 20));
return “index”;
}
/**
• 分页查询文件
*/
@GetMapping(“files/{pageIndex}/{pageSize}”)
@ResponseBody
public List listFilesByPage(@PathVariable int pageIndex, @PathVariable int pageSize) {
return fileService.listFilesByPage(pageIndex, pageSize);
}
/**
• 获取文件片信息
*/
@GetMapping(“files/{id}”)
@ResponseBody
public ResponseEntity serveFile(@RequestParam(“id”) String id) throws UnsupportedEncodingException {
Optional file = fileService.getFileById(id);
if (file.isPresent()) {
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, “attachment; fileName=” + new String(file.get().getName().getBytes(“utf-8”),“ISO-8859-1”))
.header(HttpHeaders.CONTENT_TYPE, “application/octet-stream”)
.header(HttpHeaders.CONTENT_LENGTH, file.get().getSize() + “”).header(“Connection”, “close”)
.body(file.get().getContent().getData());
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(“File was not fount”);
}
}
/**
• 在线显示文件
*/
@GetMapping(“/view”)
@ResponseBody
public ResponseEntity serveFileOnline(@RequestParam(“id”) String id) {
Optional file = fileService.getFileById(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().getData());
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(“File was not fount”);
}
}
/**
• 上传
*/
@PostMapping(“/”)
public String handleFileUpload(@RequestParam(“file”) MultipartFile file, RedirectAttributes redirectAttributes) {
try {
FileModel f = new FileModel(file.getOriginalFilename(), file.getContentType(), file.getSize(),
new Binary(file.getBytes()));
f.setMd5(MD5Util.getMD5(file.getInputStream()));
fileService.saveFile(f);
System.out.println(f);
} catch (IOException | NoSuchAlgorithmException ex) {
ex.printStackTrace();
redirectAttributes.addFlashAttribute(“message”, “Your " + file.getOriginalFilename() + " is wrong!”);
return “redirect:/”;
}
redirectAttributes.addFlashAttribute(“message”,
"You successfully uploaded " + file.getOriginalFilename() + “!”);
return “redirect:/”;
}
/**
• 上传接口
*/
@PostMapping(“/upload”)
@ResponseBody
public ResponseEntity handleFileUpload(@RequestParam(“file”) MultipartFile file) {
FileModel returnFile = null;
try {
FileModel f = new FileModel(file.getOriginalFilename(), file.getContentType(), file.getSize(),
new Binary(file.getBytes()));
f.setMd5(MD5Util.getMD5(file.getInputStream()));
returnFile = fileService.saveFile(f);
String path = “//” + serverAddress + “:” + serverPort + “/view/” + returnFile.getId();
return ResponseEntity.status(HttpStatus.OK).body(path);
} catch (IOException | NoSuchAlgorithmException ex) {
ex.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage());
}
}
/**
• 删除文件
*/
@GetMapping(“/delete”)
@ResponseBody
public ResponseEntity deleteFile( @RequestParam(“id”) String id) {
try {
fileService.removeFile(id);
return ResponseEntity.status(HttpStatus.OK).body(“DELETE Success!”);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
}
}
1.7、工具类
md5工具类:
public class MD5Util {
/**
• 获取该输入流的MD5值
*/
public static String getMD5(InputStream is) throws NoSuchAlgorithmException, IOException {
StringBuffer md5 = new StringBuffer();
MessageDigest md = MessageDigest.getInstance(“MD5”);
byte[] dataBytes = new byte[1024];
int nread = 0;
while ((nread = is.read(dataBytes)) != -1) {
md.update(dataBytes, 0, nread);
};
byte[] mdbytes = md.digest();
// convert the byte to hex format
for (int i = 0; i < mdbytes.length; i++) {
md5.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
}
return md5.toString();
}
}

1.8、前端页面

前端页面index.html:

文件服务




首页




上传文件:





文件列表

文件名 文件ID contentType 文件大小 上传时间 md5 操作 没有文件信息!!

预览| 删除

1.9、运行效果

  • 上传文件:

MongoDB保存word文件 mongodb如何存储文件_spring

MongoDB保存word文件 mongodb如何存储文件_学习_02

  • 预览
  • 下载

MongoDB保存word文件 mongodb如何存储文件_学习_03

  • 删除

MongoDB保存word文件 mongodb如何存储文件_面试_04

在文件的操作过程中,可以通过可视化工具或shell来查看存储在MongoDB中的文件:

  • 可以看到,在fileModel集合中存储了我们上传的文件,文件的内容是以二进制的形式存储

MongoDB保存word文件 mongodb如何存储文件_学习_05

2、MongoDB存储大文件


Spring Data MongoDB提供了GridFsOperations接口以及相应的实现GridFsTemplate,可以和GridFs交互。

这里在上一个工程的基础上进行改造。

2.1、依赖

和上一个工程相比,这里添加开源工具包hutool的依赖:

cn.hutool

hutool-all

4.5.1

2.2、启动类

@SpringBootApplication
public class SpringbootFileGridfsApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootFileGridfsApplication.class, args);
}
//Tomcat large file upload connection reset
@Bean
public TomcatServletWebServerFactory tomcatEmbedded() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
if ((connector.getProtocolHandler() instanceof AbstractHttp11Protocol<
?>)) {
//-1 means unlimited
((AbstractHttp11Protocol<?>) connector.getProtocolHandler()).setMaxSwallowSize(-1);
}
});
return tomcat;
}
}

TomcatServletWebServerFactory() ⽅法主要是为了解决上传文件较大时出现连接重置的问题,这个异常后台是捕捉不到的:

MongoDB保存word文件 mongodb如何存储文件_MongoDB保存word文件_06

2.3、配置

  • application.properties

MongoDB 配置

连接uri
#spring.data.mongodb.uri=mongodb://test:test@localhost:27017/filetest
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=filetest
spring.data.mongodb.username=test
spring.data.mongodb.password=test
文件上传限制
spring.servlet.multipart.max-file-size=1020MB
spring.servlet.multipart.max-request-size=1020MB
• 配置类
/**
• @Author 三分恶
• @Date 2020/1/11
• @Description
*/
@Configuration
public class MongoConfig {
//获取配置文件中数据库信息
@Value(“${spring.data.mongodb.database}”)
String db;
GridFSBucket用于打开下载流
@Bean
public GridFSBucket getGridFSBucket(MongoClient mongoClient){
MongoDatabase mongoDatabase = mongoClient.getDatabase(db);
GridFSBucket bucket = GridFSBuckets.create(mongoDatabase);
return bucket;
}
}
2.4、实体类
• 文件实体类
/**
• @Author 三分恶
• @Date 2020/1/11
• @Description
*/
@Document
public class FileDocument {
@Id // 主键
private String id;
private String name; // 文件名称
private long size; // 文件大小
private Date uploadDate; // 上传时间
private String md5; // 文件MD5值
private byte[] content; // 文件内容
private String contentType; // 文件类型
private String suffix; // 文件后缀名
private String description; // 文件描述
private String gridfsId; // 大文件管理GridFS的ID
/**
• 省略getter、setter、equales、hashCode、toString方法
*/
}
• 接口结果实体类
**
• @Author 三分恶
• @Date 2020/1/11
• @Description 公用数据返回模型
*/
public class ResponseModel {
public static final String Success = “success”;
public static final String Fail = “fail”;
private String code = “fail”;
private String message = “”;
private String data;
//私有构造函数,此类不允许手动实例化,需要调用getInstance()获取实例
private ResponseModel() {
}
/**
• 返回默认的实例
• @return
*/
public static ResponseModel getInstance() {
ResponseModel model = new ResponseModel();
model.setCode(ResponseModel.Fail);
return model;
}
/**
*省略getter/setter
*/
}
2.5、服务层
上一个实例里采用MongReposity来操作MongoDB,这里操作MongoDB采用MongoTemplate,操作GridFs采用GridFsTemplate。
/**
• @Author 三分恶
• @Date 2020/1/11
• @Description
*/
@Service
public class FileServiceImpl implements FileService {
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private GridFsTemplate gridFsTemplate;
@Autowired
private GridFSBucket gridFSBucket;
/**
• 保存文件
• @param file
• @return
*/
@Override
public FileDocument saveFile(FileDocument file) {
file = mongoTemplate.save(file);
return file;
}
/**
• 上传文件到Mongodb的GridFs中
• @param in
• @param contentType
• @return
*/
@Override
public String uploadFileToGridFS(InputStream in , String contentType){
String gridfsId = IdUtil.simpleUUID();
//将文件存储进GridFS中
gridFsTemplate.store(in, gridfsId , contentType);
return gridfsId;
}
/**
• 删除文件
• @param id
*/
@Override
public void removeFile(String id) {
//根据id查询文件
FileDocument fileDocument = mongoTemplate.findById(id , FileDocument.class );
if(fileDocument!=null){
//根据文件ID删除fs.files和fs.chunks中的记录
Query deleteFileQuery = new Query().addCriteria(Criteria.where(“filename”).is(fileDocument.getGridfsId()));
gridFsTemplate.delete(deleteFileQuery);
//删除集合fileDocment中的数据
Query deleteQuery=new Query(Criteria.where(“id”).is(id));
mongoTemplate.remove(deleteQuery,FileDocument.class);
}
}
/**
• 根据id查看文件
• @param id
• @return
*/

写在最后

很多人感叹“学习无用”,实际上之所以产生无用论,是因为自己想要的与自己所学的匹配不上,这也就意味着自己学得远远不够。无论是学习还是工作,都应该有主动性,所以如果拥有大厂梦,那么就要自己努力去实现它。

public FileDocument saveFile(FileDocument file) {
file = mongoTemplate.save(file);
return file;
}
/**
• 上传文件到Mongodb的GridFs中
• @param in
• @param contentType
• @return
*/
@Override
public String uploadFileToGridFS(InputStream in , String contentType){
String gridfsId = IdUtil.simpleUUID();
//将文件存储进GridFS中
gridFsTemplate.store(in, gridfsId , contentType);
return gridfsId;
}
/**
• 删除文件
• @param id
*/
@Override
public void removeFile(String id) {
//根据id查询文件
FileDocument fileDocument = mongoTemplate.findById(id , FileDocument.class );
if(fileDocument!=null){
//根据文件ID删除fs.files和fs.chunks中的记录
Query deleteFileQuery = new Query().addCriteria(Criteria.where(“filename”).is(fileDocument.getGridfsId()));
gridFsTemplate.delete(deleteFileQuery);
//删除集合fileDocment中的数据
Query deleteQuery=new Query(Criteria.where(“id”).is(id));
mongoTemplate.remove(deleteQuery,FileDocument.class);
}
}
/**
• 根据id查看文件
• @param id
• @retur
  • n

*/