当前位置:网站首页>57: Chapter 5: Develop admin management services: 10: Develop [get files from MongoDB's GridFS, interface]; (from GridFS, get the SOP of files) (Do not use MongoDB's service, you can exclude its autom
57: Chapter 5: Develop admin management services: 10: Develop [get files from MongoDB's GridFS, interface]; (from GridFS, get the SOP of files) (Do not use MongoDB's service, you can exclude its autom
2022-08-01 19:30:00 【small dry forest】
说明:
(1)This blog develops content:开发【从MongoDB的GridFS中,获取文件,接口】;
目录
二:开发【从MongoDB的GridFS中,获取文件,接口】;
1.在【api】接口工程中的FileUploaderControllerApi接口中,定义【从MongoDB的GridFS中,获取文件,接口】;
2.在【files】文件服务的FileUploaderController类中,去实现【从MongoDB的GridFS中,获取文件,接口】;
(1)在【api】接口工程中,创建AdminCookieTokenInterceptor拦截器;
一:本篇博客合理性说明;
本篇博客,is to develop fromGridFS中,获取文件;
即:
二:开发【从MongoDB的GridFS中,获取文件,接口】;
1.在【api】接口工程中的FileUploaderControllerApi接口中,定义【从MongoDB的GridFS中,获取文件,接口】;
/** * 【从MongoDB的GridFS中,获取文件】 * @param faceId * @param request * @param response * @return * @throws Exception */ @GetMapping("/readInGridFS") //设置路由,这个是需要前后端约定好的; public void readInGridFS(@RequestParam String faceId, HttpServletRequest request, HttpServletResponse response) throws Exception;说明:
(1)Change the request method of the interface、url、参数,都不是瞎写的,前后端需要保持一致;
(2)This interface does not require a return value;其主要作用是,从GridFS中读取文件,把文件放到Response中即可;
2.在【files】文件服务的FileUploaderController类中,去实现【从MongoDB的GridFS中,获取文件,接口】;
/** * 【从MongoDB的GridFS中,获取文件,接口】 * @param faceId * @return * @throws Exception */ @Override public void readInGridFS(String faceId, HttpServletRequest request, HttpServletResponse response) throws Exception { // 0.If the front end is passedFaceId是空,或者是"null";Just throw one"你所查看的文件不存在!"的自定义的MyCustomException异常; if (StringUtils.isBlank(faceId) || faceId.equalsIgnoreCase("null")) { GraceException.display(ResponseStatusEnum.FILE_NOT_EXIST_ERROR); } // 1. 从GridFS中读取文件; File file = readGridFSByFaceId(faceId); // 2. 把文件放到response中; FileUtils.downloadFileByStream(response, file); } /** * 工具方法:【通过id,从GridFS中,获取文件】→【保存到服务器本地】→【返回该文件,在本地服务器的file对象】 * @param faceId * @return */ private File readGridFSByFaceId(String faceId) throws Exception { /** * 1.通过gridFSBucket.find()方法,从GridFS中,查询文件; * 第一个参数"_id"表示,our inquiry,是根据id查询; * 第二个参数是,We specifically asked for thisid号; * PS:This query result may contain multiple results(For example, when we query based on other conditions,结果可能有多个),And it can be cycled; */ GridFSFindIterable gridFSFindIterable = gridFSBucket.find(Filters.eq("_id", new ObjectId(faceId))); // 2. Get the object of the single file queried;因为,我们心里清楚,上面根据id去查询,At most one can be found;所以,You can directly view only the first one of the results; GridFSFile gridFSFile = gridFSFindIterable.first(); // 3. 如果查询的结果为空,Just throw one"你所查看的文件不存在!"的自定义的MyCustomException异常; if (gridFSFile == null) { GraceException.display(ResponseStatusEnum.FILE_NOT_EXIST_ERROR); } // 4.获得文件名; String fileName = gridFSFile.getFilename(); // 5.Save the file locally on the server; // 5.1 Create a folder for saving image files locally on the server File fileTemp = new File("e:/temp_face"); if (!fileTemp.exists()) { //If the above path does not exist,就去创建; fileTemp.mkdirs(); } // 5.2 获得文件流 File myFile = new File("e:/temp_face/" + fileName); // 5.3 创建文件输出流 OutputStream os = new FileOutputStream(myFile);//这个会报FileNotFoundException异常; /** * 5.4 利用gridFSBucket.downloadToStream()方法,Save the file locally on the server; * 第一个参数:The name of the file to save to the local;我们这儿以"文件在GridFS中的id",作为"The name of the file to save to the local"; * 第二个参数:A file is an output stream; */ gridFSBucket.downloadToStream(new ObjectId(faceId), os);//downloadToStream()Methods have other refactorings,It is also possible to use other reconstruction methods; // 6. 返回该文件,在本地服务器的file对象; return myFile; }说明:
(1)首先,判断前端传过来的FaceId是空,或者是"null";
(2)然后,Call the written utility methodreadGridFSByFaceId(faceId),从GridFS中,获取文件;
(2.1)根据id,去GridFS中查询;
● GridFS中的id,需要是ObjectId(irg.bson.types)类型的;
● 然后,在查询的时候,其idType also needs to beObjectId(irg.bson.types)类型的;(PS:It's not really clear here)
(2.2)然后,从查询结果中,Get a single file object;to pass this file object,获得文件名;
(2.3)创建一个文件流、输出流;然后,会利用(2.2)The file name queried in ,to name the file name after saving the file locally;
(2.4)通过输出流,从GridFS中,下载文件到服务器;(其实,"Save the file to the local server"不是必须的;只是,Sometimes in order to facilitate troubleshooting,You can also easily save the file to a local server)
(2.5)返回,该文件的file对象;
(3)把文件放到response中;
(3.1)在【common】通用工程中,创建一个FileUtils工具类:该类主要有两个方法:【put a file stream,写入到response中】、【Put the file stream to the file,转成Base64编码的字符串】;我们这儿,The first method will be used;
package com.imooc.utils; import sun.misc.BASE64Encoder; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLDecoder; public class FileUtils { /** * 文件流下载,在浏览器展示 * @param response * @param file 文件从盘符开始的完整路径 */ public static void downloadFileByStream(HttpServletResponse response, File file) { String filePath = file.getPath(); System.out.println("filePath = " + filePath); // 对encode过的filePath处理 if (filePath.contains("%")) { try { filePath = URLDecoder.decode(filePath, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } ServletOutputStream out = null; FileInputStream in = null; try { in = new FileInputStream(file); String[] dir = filePath.split("/"); String fileName = dir[dir.length - 1]; String[] array = fileName.split("[.]"); String fileType = array[array.length - 1].toLowerCase(); // 设置文件ContentType类型 if ("jpg,jepg,gif,png".contains(fileType)) { // 判断图片类型 response.setContentType("image/" + fileType); } else if ("pdf".contains(fileType)) { // 判断pdf类型 response.setContentType("application/pdf"); } else { // 设置multipart response.setContentType("multipart/form-data"); } out = response.getOutputStream(); // 读取文件流 int len = 0; byte[] buffer = new byte[1024 * 10]; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); } out.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); in.close(); } catch (NullPointerException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } /** * 文件转换为base64 * @param file * @return */ public static String fileToBase64(File file) {//将图片文件转化为字节数组字符串,并对其进行Base64编码处理 InputStream in = null; byte[] fileData = null; // 读取文件字节数组 try { in = new FileInputStream(file); fileData = new byte[in.available()]; in.read(fileData); in.close(); } catch (IOException e) { e.printStackTrace(); } // 对字节数组Base64encode and return BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(fileData); } }(3.2)调用FileUtils工具类的downloadFileByStream()方法,把文件写入到response中;
三:一个问题:在启动【admin】管理服务和【user】用户服务的时候,报了"com.mongodb.MongoSocketOpenException: Exception opening socket";(解决办法,在【admin】和【user】启动时,在自动装配时,排除MongoDB即可)
(1)错误展示;
(2)原因分析;
其实在【43:第四章:开发文件服务:4:创建子工程【imooc-news-dev-service-files】,文件服务模块;】中,encountered this kind of problem;
● 我们在【model,模型子模块】中,引入了MongoDB的依赖;
● 而我们这个工程,【model,模型子模块】引入了【common,通用子模块】,【api,接口子模块】引入了【model,模型子模块】,【admin,管理服务】引入了【api,接口子模块】,【user,用户服务】引入了【api,接口子模块】;所以,就相当于在【admin,管理服务】和【user,用户服务】中,引入了MongoDB依赖;
● 即,【admin,管理服务】和【user,用户服务】的Spring Boot在启动的时候,Will do an autowiring,Scanning will be done during this processyml配置文件;如果我们没有在配置文件中,去配置MongoDB,It will report an error;
(3)解决办法;
方法1:在【admin,管理服务】和【user,用户服务】中,Also go to configureMongoDB;(这种方式,不太好)
方法2:在【admin,管理服务】和【user,用户服务】中,通过【@SpringBootApplication(exclude = MongoAutoConfiguration.class)】,启动Spring Boot项目的时候,排除MongoDB自动转配;
(4)效果:OK了,没有报错了;
四:【从MongoDB的GridFS中,获取文件】需要管理员登录,才能操作;(在InterceptorConfig中,把"AdminTokenInterceptor"拦截器,also acts on this interface)
五:效果;
(1)install一下整个项目;(2)然后,启动【admin】管理服务和【files】文件服务;(3)Remember to start the frontend,使用SwitchHostEnable virtual domain name;
启动【user】、【admin】、【files】这几个服务;
六:A front end problem:"cookieWhether the information in is placed in the request"时,How to accurately judge the back-end"用户的登录状态"(这个问题,can be generalized)
1.问题描述;
现在的情况:前端请求,when requesting the backend,会主动把【cookie中的aid和atoken】放在请求头中,send to backend;
如此一来,We wrote earlierAdminTokenInterceptor拦截器,在拦截【从MongoDB的GridFS中,获取文件,接口】的时候,can be obtained from the request headeraid和atoken的;
……………………………………………………
即,此时,We are intercepting this interface,从请求头中获取aid和atoken;Make this interface only when the administrator is logged in,才能调用;是OK的;
但是,如果前端请求,when requesting the backend,没有把【cookie中的aid和atoken】放在请求头中,Just ask directly【从MongoDB的GridFS中,获取文件,接口】接口了;
那么此时,【We wrote earlierAdminTokenInterceptor拦截器,在拦截【从MongoDB的GridFS中,获取文件,接口】的时候,Could not get it from the request headeraid和atoken的;;;即,The interceptor will determine,Currently the administrator is not logged in】→【但是,此时我们,Obviously an administrator is logged in】→【为此,We need a remedial strategy:如果请求头中没有aid和atoken的话,We just need to try directly from cookie中获取aid和atoken】→【自然,这儿的逻辑是,If the front end already does this,How do we remediate and be compatible in the backend;;;后续,when conditions permit,After front-end and back-end coordination,can be improved】
2.补救策略;
(1)在【api】接口工程中,创建AdminCookieTokenInterceptor拦截器;
package com.imooc.api.interceptors; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class AdminCookieTokenInterceptor extends BaseInterceptor implements HandlerInterceptor { /** * 拦截请求,在访问controller调用之前 * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String adminUserId = getCookie(request, "aid"); String adminUserToken = getCookie(request, "atoken"); boolean run = verifyUserIdToken(adminUserId, adminUserToken, REDIS_ADMIN_TOKEN); return run; } }说明:
(1)我们在BaseInterceptor中,A utility method is written"直接从cookie中获取值";
(2)这个拦截器,is directly from the request,获取某个cookie的值;然后,再去调用BaseInterceptor中的"工具方法:验证前端的登录信息是否OK;"
(2)使用;(存在疑问……待完善)
If an interface is like this:【This interface needs to verify the login status】、【该系统,will send the login information,保存在cookie中】;
● 情况1:策略:然后,如果前端请求,when requesting the backend,会主动把【cookie中的aid和atoken】放在请求头中,send to backend;Then use the previous one directlyAdminTokenInterceptor拦截器,去处理就行了;
● 情况2:策略:然后,如果前端请求,when requesting the backend,没有把【cookie中的aid和atoken】放在请求头中,Just go directly to the request interface;那么,我们就可以使用AdminCookieTokenInterceptor拦截器,来处理了;
● 情况3:策略1:然后,如果前端请求,when requesting the backend,Unable to determine it"会主动把【cookie中的aid和atoken】放在请求头中"还是"没有把【cookie中的aid和atoken】放在请求头中";那么,We seem to be able to handle it this way:Use an interceptor,Just put the logic of the two interceptors above,put in an interceptor;
● 情况3:策略2:然后,如果前端请求,when requesting the backend,Unable to determine it"会主动把【cookie中的aid和atoken】放在请求头中"还是"没有把【cookie中的aid和atoken】放在请求头中";那么,We seem to be able to handle it this way:Keep two interceptors,Use these two interceptors to handle this request;
但是,After some thought,Found out that it didn't seem necessary to do so;因为这样做,It will become a bit unreasonable logically、不太适合;
边栏推荐
猜你喜欢
随机推荐
ClassID的计算中,&表示啥意思
Become a Contributor in 30 minutes | How to participate in OpenHarmony's open source contributions in multiple ways?
内网穿透 lanproxy部署
What are the application advantages of SaaS management system?How to efficiently improve the digital and intelligent development level of food manufacturing industry?
ExcelPatternTool: Excel form-database mutual import tool
1个小时!从零制作一个! AI图片识别WEB应用!
Selenium在远程中的截图
Win11怎么安装语音包?Win11语音包安装教程
log factory (detail)
选择合适的 DevOps 工具,从理解 DevOps 开始
mysql函数的作用有哪些
手撸代码,Redis发布订阅机制实现
分享一个适用于MCU项目的代码框架
如何记录分析你的炼丹流程—可视化神器Wandb使用笔记【1】
MySQL中超键、主键及候选键的区别是什么
#yyds dry goods inventory# Interview must brush TOP101: the last k nodes in the linked list
为你的“架构”安排定期体检吧!
Hardware Bear Original Collection (Updated 2022/07)
【1374. 生成每种字符都是奇数个的字符串】
SQL的 ISNULL 函数










































