SpringBoot学习笔记(十一:使用MongoDB存储文件 ),网易后端面试

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

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、运行效果

  • 上传文件:

在这里插入图片描述

在这里插入图片描述

  • 预览

在这里插入图片描述

  • 下载

在这里插入图片描述

  • 删除

在这里插入图片描述

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

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

在这里插入图片描述

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

在这里插入图片描述

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

*/

@Override

public Optional getFileById(String id){

FileDocument fileDocument = mongoTemplate.findById(id , FileDocument.class );

if(fileDocument != null){

Query gridQuery = new Query().addCriteria(Criteria.where(“filename”).is(fileDocument.getGridfsId()));

try {

//根据id查询文件

GridFSFile fsFile = gridFsTemplate.findOne(gridQuery);

//打开流下载对象

GridFSDownloadStream in = gridFSBucket.openDownloadStream(fsFile.getObjectId());

if(in.getGridFSFile().getLength() > 0){

//获取流对象

GridFsResource resource = new GridFsResource(fsFile, in);

//获取数据

fileDocument.setContent(IoUtil.readBytes(resource.getInputStream()));

return Optional.of(fileDocument);

}else {

fileDocument = null;

return Optional.empty();

}

}catch (IOException ex){

ex.printStackTrace();

}

}

return Optional.empty();

}

/**

  • 分页列出文件

  • @param pageIndex

  • @param pageSize

  • @return

*/

@Override

public List listFilesByPage(int pageIndex, int pageSize) {

Query query = new Query().with(Sort.by(Sort.Direction.DESC, “uploadDate”));

long skip = (pageIndex -1) * pageSize;

query.skip(skip);

query.limit(pageSize);

Field field = query.fields();

field.exclude(“content”);

List files = mongoTemplate.find(query , FileDocument.class );

return files;

}

}

2.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(@PathVariable 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());

} 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”)

.header(HttpHeaders.CONTENT_LENGTH , file.get().getSize() + “”)

.body(file.get().getContent());

} else {

return ResponseEntity.status(HttpStatus.NOT_FOUND).body(“File was not found”);

}

}

/**

  • 上传

*/

@PostMapping(“/”)

public String handleFileUpload(@RequestParam(“file”) MultipartFile file, RedirectAttributes redirectAttributes) {

try {

FileDocument fileDocument = new FileDocument();

fileDocument.setName(file.getOriginalFilename());

fileDocument.setSize(file.getSize());

fileDocument.setContentType(file.getContentType());

fileDocument.setUploadDate(new Date());

String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(“.”));

fileDocument.setSuffix(suffix);

fileDocument.setMd5(MD5Util.getMD5(file.getInputStream()));

//将文件存入gridFs

String gridfsId = fileService.uploadFileToGridFS(file.getInputStream() , file.getContentType());

fileDocument.setGridfsId(gridfsId);

fileDocument = fileService.saveFile(fileDocument);

System.out.println(fileDocument);

} 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) {

FileDocument returnFile = null;

try {

FileDocument fileDocument = new FileDocument();

fileDocument.setName(file.getOriginalFilename());

fileDocument.setSize(file.getSize());

fileDocument.setContentType(file.getContentType());

fileDocument.setUploadDate(new Date());

String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(“.”));

fileDocument.setSuffix(suffix);

fileDocument.setMd5(MD5Util.getMD5(file.getInputStream()));

//将文件存入gridFs

String gridfsId = fileService.uploadFileToGridFS(file.getInputStream() , file.getContentType());

fileDocument.setGridfsId(gridfsId);

returnFile = fileService.saveFile(fileDocument);

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 ResponseModel deleteFile( @RequestParam(“id”) String id) {

ResponseModel model = ResponseModel.getInstance();

if(!StrUtil.isEmpty(id)){

fileService.removeFile(id);

model.setCode(ResponseModel.Success);

model.setMessage(“删除成功”);

}else {

model.setMessage(“请传入文件id”);

}

return model;

}

}

  • 前端页面没有变动。

2.7、运行效果

  • 上传文件

这里我们选择一个比较大的mp4文件

在这里插入图片描述在这里插入图片描述

  • 预览

预览还存在问题,后台会报错:org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class edu.hpu.domain.ResponseModel] with preset Content-Type ‘video/mp4’

待解决

在这里插入图片描述

  • 下载

在这里插入图片描述

  • 删除

在这里插入图片描述

在上传和删除数据的过程中,可以通过可视化工具或shell来查看MongoDB中的数据

  • fileDocment中的数据:fileDocment是一个普通的集合,对应地以文档的形式存储了FileDocument实例

在这里插入图片描述

  • fs.files中的数据:文件的元数据

在这里插入图片描述

  • fs.chunks中的数据:file被切分成若干个chunks,存储了文件的二进制数据

在这里插入图片描述


本文为学习笔记类博客,学习资料来源见参考!

【1】:MongoDB GridFS

【2】:Mongodb的文件存储GridFs

【3】:MongoDB学习笔记(五) MongoDB文件存取操作

【4】:《MongoDB大数据权威处理指南》

【5】:java文件转二进制

【6】:Java将文件转为字节数组

【7】:java文件下载的几种方式

总结:绘上一张Kakfa架构思维大纲脑图(xmind)

image

其实关于Kafka,能问的问题实在是太多了,扒了几天,最终筛选出44问:基础篇17问、进阶篇15问、高级篇12问,个个直戳痛点,不知道如果你不着急看答案,又能答出几个呢?

若是对Kafka的知识还回忆不起来,不妨先看我手绘的知识总结脑图(xmind不能上传,文章里用的是图片版)进行整体架构的梳理

梳理了知识,刷完了面试,如若你还想进一步的深入学习解读kafka以及源码,那么接下来的这份《手写“kafka”》将会是个不错的选择。

  • Kafka入门

  • 为什么选择Kafka

  • Kafka的安装、管理和配置

  • Kafka的集群

  • 第一个Kafka程序

  • Kafka的生产者

  • Kafka的消费者

  • 深入理解Kafka

  • 可靠的数据传递

  • Spring和Kafka的整合

  • SpringBoot和Kafka的整合

  • Kafka实战之削峰填谷

  • 数据管道和流式处理(了解即可)

image

image

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
ongoDB中的数据**

  • fileDocment中的数据:fileDocment是一个普通的集合,对应地以文档的形式存储了FileDocument实例

在这里插入图片描述

  • fs.files中的数据:文件的元数据

在这里插入图片描述

  • fs.chunks中的数据:file被切分成若干个chunks,存储了文件的二进制数据

在这里插入图片描述


本文为学习笔记类博客,学习资料来源见参考!

【1】:MongoDB GridFS

【2】:Mongodb的文件存储GridFs

【3】:MongoDB学习笔记(五) MongoDB文件存取操作

【4】:《MongoDB大数据权威处理指南》

【5】:java文件转二进制

【6】:Java将文件转为字节数组

【7】:java文件下载的几种方式

总结:绘上一张Kakfa架构思维大纲脑图(xmind)

[外链图片转存中…(img-E0sZPNEE-1713572893821)]

其实关于Kafka,能问的问题实在是太多了,扒了几天,最终筛选出44问:基础篇17问、进阶篇15问、高级篇12问,个个直戳痛点,不知道如果你不着急看答案,又能答出几个呢?

若是对Kafka的知识还回忆不起来,不妨先看我手绘的知识总结脑图(xmind不能上传,文章里用的是图片版)进行整体架构的梳理

梳理了知识,刷完了面试,如若你还想进一步的深入学习解读kafka以及源码,那么接下来的这份《手写“kafka”》将会是个不错的选择。

  • Kafka入门

  • 为什么选择Kafka

  • Kafka的安装、管理和配置

  • Kafka的集群

  • 第一个Kafka程序

  • Kafka的生产者

  • Kafka的消费者

  • 深入理解Kafka

  • 可靠的数据传递

  • Spring和Kafka的整合

  • SpringBoot和Kafka的整合

  • Kafka实战之削峰填谷

  • 数据管道和流式处理(了解即可)

[外链图片转存中…(img-rzFFBfbr-1713572893822)]

[外链图片转存中…(img-r6oKtw9V-1713572893822)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-yly8OJBW-1713572893822)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 20
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值