当前位置: 首页 > news >正文

Java工程师实现视频文件上传minio文件系统存储及网页实现分批加载视频播放

Java工程师实现minio存储大型视频文件网页实现分批加载视频播放

在这里插入图片描述

一、需求说明

老板给我出个题目,让我把的电影文件上传到minio文件系统,再通过WEB端分配加载视频播放,类似于我们普通的电影网站。小编把Java代码共享出来。是真正的能拿过来直接用的。小编我亲自测过。

1.1minio版本

<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>7.1.4</version></dependency>

1.2maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.sun</groupId><artifactId>springboot-sun</artifactId><version>1.0-SNAPSHOT</version><!--springboot工程需要继承的父工程--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.8.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!--web开发的起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><!--<scope>runtime</scope>--></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>7.1.4</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version><scope>provided</scope></dependency><dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId><version>2.10.12</version>
</dependency></dependencies></project>

1.3分段视频播放Controller

package com.sun.controller;import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.StatObjectArgs;
import io.minio.errors.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;import com.sun.minio.util.MinIoUtil;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;@RestController
public class VideoController2 {@GetMapping("/testDisplay2/{fileName}")public ResponseEntity<byte[]> getVideo(@PathVariable String fileName,@RequestHeader(value = "Range", required = false) String range) {try {String bucketName = "movie";String objectName = fileName + ".mp4";MinioClient minioClient= MinIoUtil.minioClient;// 根据Range请求头来设置分批加载的范围GetObjectArgs.Builder getObjectArgsBuilder = GetObjectArgs.builder().bucket(bucketName).object(objectName);long start=0l;if (range!= null && range.startsWith("bytes=")) {String[] values = range.split("=")[1].split("-");start = Long.parseLong(values[0]);long end = values.length > 1? Long.parseLong(values[1]) : -1;if (end!= -1) {long length = end - start + 1;getObjectArgsBuilder.offset(start).length(length);} else {getObjectArgsBuilder.offset(start);}}// 获取视频流try (InputStream inputStream = minioClient.getObject(getObjectArgsBuilder.build())) {ByteArrayOutputStream outputStream = new ByteArrayOutputStream();byte[] buffer = new byte[1024 * 1024]; // 1MB缓冲区int length;while ((length = inputStream.read(buffer))!= -1) {outputStream.write(buffer, 0, length);}byte[] videoBytes = outputStream.toByteArray();HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.parseMediaType("video/mp4"));// 如果是分段请求,设置相应的响应头if (range!= null) {ObjectStat objectStat = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());long fileSize = objectStat.length();headers.set("Content-Range", "bytes " + start + "-" + (start + videoBytes.length - 1) + "/" + fileSize);return new ResponseEntity<>(videoBytes, headers, HttpStatus.PARTIAL_CONTENT);} else {headers.setContentLength(videoBytes.length);return new ResponseEntity<>(videoBytes, headers, HttpStatus.OK);}}} catch (Exception e) {e.printStackTrace();return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);}}
}

1.4 html5 播放

<html>
<title>test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<head><style type="text/css">.upload {margin-top: 100px;margin-left: 100px;text-align: center;}</style></head>
<body>
<h1 style="text-align: center;margin-top: 20px">test</h1>
<div><!--  <img src="/minio_demo/testClick/testDisplay?url=/data/temp/img/a6efce1b9d16fdfabf36882ab08f8c5495ee7b9f.jpg"> -->
</div><div><!--   <video src="/minio_demo/testClick/testDisplay?md5=a172e15a869fd7224618840c0815dcb1" controls width="640" height="360">您的浏览器不支持视频标签。
</video>--><video src="/minio_demo/testDisplay2/test" controls width="640" height="360">您的浏览器不支持视频标签。
</video></div>
</body>
</html>

1.5上传Controller

 package com.sun.controller;import io.minio.errors.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;import com.sun.minio.util.MinIoContentType;
import com.sun.minio.util.MinIoUtil;
import com.sun.minio.util.MinioBucketEnum;
import com.sun.service.MinioServiceImpl;import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Properties;@RestController
@RequestMapping("testClick")
public class MinioDemoController {@Autowiredprivate MinioServiceImpl minioServiceImpl;/*** 直接页面展示* @param response*/@RequestMapping("/testDisplay")public void testDisplay(HttpServletResponse response,String md5) throws Exception {MinIoUtil.displayFile(minioServiceImpl,MinioBucketEnum.VIDEO_FILES,response,md5);}@RequestMapping("/testDownload")public void testDownLoad(HttpServletResponse response,String md5) throws Exception {MinIoUtil.downloadFile(minioServiceImpl,MinioBucketEnum.VIDEO_FILES,response,md5,"");}@RequestMapping("/testLoad")public void testLoadFile(HttpServletResponse response,String md5,String targetPath) throws  Exception{MinIoUtil.loadObject(MinioBucketEnum.VIDEO_FILES,targetPath,md5);}@RequestMapping("/deleteFile")public String deleteFile(String md5) throws  Exception{MinIoUtil.deleteObject(minioServiceImpl,MinioBucketEnum.VIDEO_FILES, md5);return "deleteFileSucceed";}@RequestMapping("/testUpload")@ResponseBodypublic String testUpload(HttpServletRequest request, @RequestParam("multipartFile") MultipartFile[] multipartfiles,String url,String pucketName)throws  Exception{String filePath = "";for(MultipartFile multipartFile:multipartfiles){String contentType = multipartFile.getContentType();InputStream inputStream = multipartFile.getInputStream();if(!StringUtils.isBlank(url)&&!url.startsWith("/")){url = "/"+url;}if(!StringUtils.isBlank(url)&&!url.endsWith("/")){url += "/";}MinioBucketEnum minioBucketEnum = MinioBucketEnum.VIDEO_FILES;/** if(pucketName.equals("monthlytext")){ minioBucketEnum =* MinioBucketEnum.MONTHLY_TEXT; } if(pucketName.equals("email")){* minioBucketEnum = MinioBucketEnum.VIDEO_FILES; }* if(pucketName.equals("excel")){ minioBucketEnum = MinioBucketEnum.EXCEL; }*/String md5 = MinIoUtil.upload(minioServiceImpl,minioBucketEnum,url + multipartFile.getOriginalFilename(), inputStream, MinIoContentType.getContentType(contentType));filePath+="<p>"+md5+"</p>";if(multipartFile.getOriginalFilename().contains(".mp4")) {filePath = "<video src=\"/minio_demo/testClick/testDisplay?md5="+md5+"\" controls width=\"640\" height=\"360\">\n" +"    您的浏览器不支持视频标签。\n" +"</video>";}// 构建包含两个a标签的HTML代码字符串StringBuilder htmlBuilder = new StringBuilder();htmlBuilder.append("<a href=\"/minio_demo/testClick/deleteFile?md5=").append(md5).append("\">删除文件</a>");htmlBuilder.append("&nbsp;&nbsp;&nbsp;&nbsp;"); // 增加一些空格间隔两个标签htmlBuilder.append("<a href=\"/minio_demo/testClick/testDownload?md5=").append(md5).append("\">下载文件</a>");// 得到最终的HTML字符串String htmlString = htmlBuilder.toString();filePath=filePath+htmlString;// filePath+="<p>"+md5+"</p>";}return filePath;}@RequestMapping("/testMv1")public ModelAndView testMv1(HttpServletRequest request){ModelAndView modelAndView = new ModelAndView();modelAndView.setViewName("/minio/demo");return modelAndView;}@RequestMapping("/testMv2")public ModelAndView testMv2(HttpServletRequest request){ModelAndView modelAndView = new ModelAndView();modelAndView.setViewName("/minio/demo2");return modelAndView;}@RequestMapping("/testMv3")public ModelAndView testMv3(){ModelAndView mv = new ModelAndView();mv.setViewName("/minio/demo3");return mv;}
}

1.6 上传HTML5

<!DOCTYPE html>
<html lang="zh-CN">
<head><title>文件上传页面</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><style type="text/css">/* 整体页面样式 */body {font-family: Arial, sans-serif;margin: 0;padding: 0;display: flex;flex-direction: column;justify-content: center;align-items: center;min-height: 100vh;background-color: #f4f4f4;}/* 标题样式 */h1 {color: #333;margin-bottom: 30px;}/* 上传表单容器样式 */.upload {background-color: #fff;padding: 30px;border-radius: 10px;box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);width: 400px;}/* 表单段落样式 */.upload p {margin-bottom: 20px;}/* 文件选择输入框样式 */.upload input[type="file"] {width: 100%;padding: 10px;border: 1px solid #ccc;border-radius: 5px;}/* 文本输入框样式 */.upload input[type="text"] {width: 100%;padding: 10px;border: 1px solid #ccc;border-radius: 5px;}/* 提交按钮样式 */.upload input[type="submit"] {background-color: #007BFF;color: #fff;padding: 10px 20px;border: none;border-radius: 5px;cursor: pointer;transition: background-color 0.3s ease;}.upload input[type="submit"]:hover {background-color: #0056b3;}</style>
</head>
<body><h1>文件上传至MinIO</h1><div class="upload"><form action="/minio_demo/testClick/testUpload" method="post" enctype="multipart/form-data"><p>选择文件: <input type="file" name="multipartFile" /></p><p>桶名称: <input type="text" name="pucketName" value="movie" /></p><p>文件目录: <input type="text" name="url" hidden /></p><p><input type="submit" value="上传并检测" /></p></form></div>
</body>
</html>

在这里插入图片描述

1.7 MinIoUtil工具类

package com.sun.minio.util;import io.minio.*;
import io.minio.errors.*;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.web.multipart.MultipartFile;import com.sun.doman.MinioRecord;
import com.sun.service.MinioServiceImpl;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.*;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;/*** minio工具类*//*** minio工具类*/
public class MinIoUtil {private static MinioServiceImpl minioService = null;private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(MinIoUtil.class);public static MinioClient minioClient = null;private static  String enpoint;private static  String username;private static  String password;static {try{Properties properties = PropertiesLoaderUtils.loadAllProperties("application.properties");enpoint = properties.getProperty("minioEnpoint");Logger.info("获取minio[enpoint]:"+enpoint);username = properties.getProperty("minioUserName");Logger.info("获取minio[minioUserName]:"+username);password = properties.getProperty("minioPassword");Logger.info("获取minio[minioPassword]"+password);minioClient = MinioClient.builder().endpoint(enpoint).credentials(username,password).build();for(MinioBucketEnum minioBucketEnum: MinioBucketEnum.values()){if(!bucketExist(minioBucketEnum.getBucketName())){createBucket(minioBucketEnum.getBucketName());}}}catch (Exception e){Logger.error("获取minio连接失败",e);e.printStackTrace();}}//检查桶是否存在public static boolean bucketExist(String buckeyName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {return minioClient.bucketExists(BucketExistsArgs.builder().bucket(buckeyName).build());}//创建桶public static  void createBucket(String bucketName) throws IOException, InvalidKeyException, InvalidResponseException, RegionConflictException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}/**** @return*/public Map<String,String> getAccess(){Map<String,String> map = new HashMap<>();map.put("username",username);map.put("password",password);return map;}/**** @param name 文件名,可以是单纯的文件名test.pdf,也可以是类似aaa/bbb/ccc/test.pdf,如果没有重复文件名,则会为在minio中的文件路径* @param inputStream 文件流* @param contentType* @return*/public static String upload(MinioServiceImpl minioService ,MinioBucketEnum minioBucketEnum, String name, InputStream inputStream, MinIoContentType contentType)throws Exception{if(StringUtils.isBlank(name)||inputStream==null){return null;}try{BufferedInputStream bis = new BufferedInputStream(inputStream);String md5Val = MD5Util.md5HashCode32(bis);MinioRecord byMd5Val = minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);if(byMd5Val==null){if(objectExist(minioBucketEnum.getBucketName(),name)){//minio存在同名文件,需要改一下文件名name = getOtherName(name,md5Val);}boolean flag = false;int count = 3;for(int i = 0;i<count;i++){//失败尝试3次.最后一次还失败抛异常if(flag){break;}try{minioClient.putObject(PutObjectArgs.builder().bucket(minioBucketEnum.getBucketName()).object(name).contentType(contentType == null ? MinIoContentType.STREAM.contentType : contentType.contentType).stream(bis, bis.available(), partSize).build());flag = true;}catch (Exception e){if (i==count-1){throw e;}TimeUnit.MILLISECONDS.sleep(200);}}minioService.insertOne(MinioRecord.builder().bucketName(minioBucketEnum.getBucketName()).md5Val(md5Val).minioFilePath(name).remainNum(1).createTime(DateTime.now().toString("yyyy-MM-dd HH:mm:ss")).updateTime(DateTime.now().toString("yyyy-MM-dd HH:mm:ss")).build());}else{byMd5Val.setRemainNum((byMd5Val.getRemainNum()==null?0:byMd5Val.getRemainNum())+1);minioService.updateRemainNum(byMd5Val);}return md5Val;}catch (Exception e){Logger.error("上传文件失败,name:"+name,e);throw e;}finally {try{inputStream.close();}catch (Exception e){}}}public static String upload(MinioBucketEnum minioBucketEnum, String name, InputStream inputStream, MinIoContentType contentType)throws Exception{if(StringUtils.isBlank(name)||inputStream==null){return null;}try{BufferedInputStream bis = new BufferedInputStream(inputStream);String md5Val = MD5Util.md5HashCode32(bis);MinioRecord byMd5Val = minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);if(byMd5Val==null){if(objectExist(minioBucketEnum.getBucketName(),name)){//minio存在同名文件,需要改一下文件名name = getOtherName(name,md5Val);}boolean flag = false;int count = 3;for(int i = 0;i<count;i++){//失败尝试3次.最后一次还失败抛异常if(flag){break;}try{minioClient.putObject(PutObjectArgs.builder().bucket(minioBucketEnum.getBucketName()).object(name).contentType(contentType == null ? MinIoContentType.STREAM.contentType : contentType.contentType).stream(bis, bis.available(), partSize).build());flag = true;}catch (Exception e){if (i==count-1){throw e;}TimeUnit.MILLISECONDS.sleep(200);}}minioService.insertOne(MinioRecord.builder().bucketName(minioBucketEnum.getBucketName()).md5Val(md5Val).minioFilePath(name).remainNum(1).createTime(DateTime.now().toString("yyyy-MM-dd HH:mm:ss")).updateTime(DateTime.now().toString("yyyy-MM-dd HH:mm:ss")).build());}else{byMd5Val.setRemainNum((byMd5Val.getRemainNum()==null?0:byMd5Val.getRemainNum())+1);minioService.updateRemainNum(byMd5Val);}return md5Val;}catch (Exception e){Logger.error("上传文件失败,name:"+name,e);throw e;}finally {try{inputStream.close();}catch (Exception e){}}}/**** @param name 文件名,可以是单纯的文件名test.pdf,也可以是类似aaa/bbb/ccc/test.pdf,如果没有重复文件名,则会为在minio中的文件路径* @param filePath 需要上传的文件路径* @param contentType* @return*/public static String upload(MinioBucketEnum minioBucketEnum, String name, String filePath, MinIoContentType contentType) throws Exception {File file = new File(filePath);if(!file.exists()||minioBucketEnum==null){return null;}return upload(minioBucketEnum,name, new FileInputStream(file), contentType);}/*** 基于MultipartFile文件上传* @param minioBucketEnum 桶信息* @param file 文件* @param contentType 内容类型* @return 返回结果* @throws Exception 异常*/public static String upload(@NotNull MinioBucketEnum minioBucketEnum, @NotNull MultipartFile file, MinIoContentType contentType) throws Exception {String filename = file.getOriginalFilename();byte[] bytes = file.getBytes();ByteArrayInputStream bais = new ByteArrayInputStream(bytes);String md5Str = upload(minioBucketEnum, filename, bais, contentType);bais.close();return md5Str;}/**** @param response* @param md5ValOrOldPath 新文件上传则是md5值,旧文件则依然使用旧文件的路径* @param exportFileName 最终给到前端的文件名*/public static void downloadFile(MinioServiceImpl minioService ,MinioBucketEnum minioBucketEnum, HttpServletResponse response, String md5ValOrOldPath,String exportFileName){downloadFileByMd5(minioService,minioBucketEnum,response,checkOutMd5Val(md5ValOrOldPath),exportFileName);}/*** 根据文件md5值获取* @param md5Val* @return*/private static void downloadFileByMd5(MinioServiceImpl minioService ,MinioBucketEnum minioBucketEnum, HttpServletResponse response, String md5Val,String exportFileName){if(StringUtils.isBlank(md5Val)||response==null){return ;}try {MinioRecord byMd5Val = minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);if(byMd5Val!=null){ObjectStat objectStat = minioClient.statObject(StatObjectArgs.builder().bucket(minioBucketEnum.getBucketName()).object(byMd5Val.getMinioFilePath()).build());DownloadUtils.downloadFile(response, minioClient.getObject(GetObjectArgs.builder().bucket(minioBucketEnum.getBucketName()).object(byMd5Val.getMinioFilePath()).build()), objectStat.name(), objectStat.length(), objectStat.contentType(), true, -1,exportFileName);}} catch (Exception e) {Logger.error("下载文件失败",e);e.printStackTrace();}}/**** @param response* @param md5ValOrOldPath 新文件上传则是md5值,旧文件则依然使用旧文件的路径,最终都是通过文件md5找文件* @throws Exception*/public static void displayFile(MinioServiceImpl minioService ,MinioBucketEnum minioBucketEnum, HttpServletResponse response, String md5ValOrOldPath) throws Exception {displayFileByMd5( minioService ,minioBucketEnum,response,checkOutMd5Val(md5ValOrOldPath));}/*** 根据MD5值展示文件* @param response* @param md5Val*/private static void displayFileByMd5(MinioServiceImpl minioService ,MinioBucketEnum minioBucketEnum, HttpServletResponse response, String md5Val) throws Exception {MinioRecord byMd5Val = minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);ObjectStat objectStat = minioClient.statObject(StatObjectArgs.builder().bucket(minioBucketEnum.getBucketName()).object(byMd5Val.getMinioFilePath()).build());String objectName = objectStat.name();String fileName = objectName.substring(objectName.lastIndexOf("/")+1);response.setCharacterEncoding("utf-8");response.setHeader("Content-Type",objectStat.contentType());response.addHeader("Content-Disposition","inline;filename=" +  URLEncoder.encode(fileName, "UTF-8"));response.addHeader("Content-Length","" + objectStat.length());try(ServletOutputStream outputStream = response.getOutputStream();BufferedInputStream bufferedInputStream = new BufferedInputStream(getObjectInputStream(minioBucketEnum.getBucketName(),objectName));BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);){int lenth;byte[] bytes = new byte[1024];while ((lenth=bufferedInputStream.read(bytes,0,bytes.length))!=-1){bufferedOutputStream.write(bytes,0,lenth);}bufferedOutputStream.flush();}catch (Exception e){Logger.error("下载失败objectname:"+objectName,e);}}/**** @param name* @param md5Val* @return*/private static String getOtherName(String name,String md5Val){try{String preFix = name.substring(0,name.lastIndexOf("/")+1);if(!name.contains(".")){return preFix+md5Val;}String suffix = name.substring(name.lastIndexOf("."));return preFix+md5Val+suffix;}catch (Exception e){//最起码把md5的值返回作为文件名return md5Val;}}/*** 文件下载,可以下载到本地某个地址* @param targetFilaPath 目标文件* @param md5ValOrOldPath 要下载到的目标文件地址,文件如果已经存在就无法下载* @throws Exception*/public static void loadObject(MinioBucketEnum minioBucketEnum, String targetFilaPath, String md5ValOrOldPath) throws Exception{loadOBbjectByMD5(minioBucketEnum,targetFilaPath,checkOutMd5Val(md5ValOrOldPath));}/*** 文件下载,可以下载到本地某个地址* @param targetFilePath 要下载到的目标文件地址,文件如果已经存在就无法下载* @throws Exception*/private static void loadOBbjectByMD5(MinioBucketEnum minioBucketEnum, String targetFilePath, String md5Val) throws Exception {if(StringUtils.isBlank(targetFilePath)){return ;}MinioRecord byMd5Val = minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);if(byMd5Val==null){return ;}File file = new File(targetFilePath);if(file.exists()){return ;}file.createNewFile();loadOBbjectByMD5(minioBucketEnum,new FileOutputStream(file),md5Val);}/**** @param os* @param md5ValOrOldPath 新文件上传则是md5值,旧文件则依然使用旧文件的路径,最终都是通过文件md5找文件* @throws Exception*/public static void loadObject(MinioBucketEnum minioBucketEnum, OutputStream os, String md5ValOrOldPath) throws Exception{//先判断是不是32位的md5值loadOBbjectByMD5(minioBucketEnum,os,checkOutMd5Val(md5ValOrOldPath));}/*** 看下传入参数是否是md5值,为md5值则直接返回..如果是旧文件系统保留的文件路径,需要看下迁移的文件中是否有过该文件,有则返回该文件的实际md5值* 虽然可能会查两次数据库,不过问题不大,这表数据并不大* @param md5ValOrOldPath* @return*/private static String checkOutMd5Val(String md5ValOrOldPath){//先判断是不是32位的md5值if(StringUtils.isBlank(md5ValOrOldPath)){return "";}if(md5ValOrOldPath.length()!=32||md5ValOrOldPath.contains(".")||md5ValOrOldPath.contains("/")||md5ValOrOldPath.contains("\\")){String code = MD5Util.md5HashCode32Str(md5ValOrOldPath);MinioRecord byOldPath = minioService.getByOldPathMD5(code);if(byOldPath==null){return "";}md5ValOrOldPath = byOldPath.getMd5Val();}return md5ValOrOldPath;}/*** 文件下载* @param os 目标输出流* @throws Exception*/private static boolean loadOBbjectByMD5(MinioBucketEnum minioBucketEnum, OutputStream os, String md5Val)throws Exception{if(StringUtils.isBlank(md5Val)||os==null){return false;}MinioRecord byMd5Val = minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);if(byMd5Val==null){return false;}try(BufferedInputStream bufferedInputStream = new BufferedInputStream(getObjectInputStream(minioBucketEnum.getBucketName(),byMd5Val.getMinioFilePath()));BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(os);){int lenth;byte[] bytes = new byte[1024];while ((lenth=bufferedInputStream.read(bytes,0,bytes.length))!=-1){bufferedOutputStream.write(bytes,0,lenth);}bufferedOutputStream.flush();return true;}catch (Exception e){Logger.error("下载失败objname:"+byMd5Val.getMinioFilePath(),e);throw e;}}/*** true存在* @param bucketName* @param name* @return*/private static boolean objectExist(String bucketName,String name)throws Exception{try{ObjectStat objectStat = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(name).build());if(!StringUtils.isBlank(objectStat.name())){return true;}return false;}catch (Exception e){return false;}}private static final long partSize = 1024*1024*5l;/*** 覆盖并上传文件* @param bucketName* @param name* @param inputStream* @throws Exception*//*** 获取对象流* @param bucketName* @param objectName* @return*/private static InputStream getObjectInputStream(String bucketName,String objectName) throws Exception{int count = 3;for(int i = 0;i<count;i++){try{InputStream object = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());return object;}catch (Exception e2){if(i==count-1){throw e2;}TimeUnit.MILLISECONDS.sleep(200);}}return null;}/*** 获取对象流* @param bucketName* @param objectName* @return*/public static InputStream getObjectInputStreamByMd5Val(String bucketName,String objectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {MinioRecord byMd5Val = minioService.getByMd5Val(bucketName, objectName);InputStream object = null;try {object = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(byMd5Val.getMinioFilePath()).build());} catch (Exception e) {e.printStackTrace();object = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(byMd5Val.getMinioFilePath()).build());}return object;}/*** 根据md5值删除* 新文件上传则是md5值,旧文件则依然使用旧文件的路径,最终都是通过文件md5找文件* @return*/public static void deleteObject(MinioServiceImpl minioService,MinioBucketEnum minioBucketEnum, String md5ValOrOldPath) throws Exception{String md5Val = checkOutMd5Val(md5ValOrOldPath);MinioRecord byMd5Val = minioService.getByMd5Val(minioBucketEnum.getBucketName(),md5Val);if(byMd5Val==null){return;}if(byMd5Val.getRemainNum()!=null&&byMd5Val.getRemainNum()<=1){if(objectExist(minioBucketEnum.getBucketName(),byMd5Val.getMinioFilePath())){minioClient.removeObject(RemoveObjectArgs.builder().bucket(minioBucketEnum.getBucketName()).object(byMd5Val.getMinioFilePath()).build());minioService.deleteRecord(minioBucketEnum.getBucketName(),md5Val);}}else{Integer remainNum = byMd5Val.getRemainNum();if(remainNum==null){byMd5Val.setRemainNum(1);}else{byMd5Val.setRemainNum(remainNum-1);}minioService.updateRemainNum(byMd5Val);}}/*** 仅供判断该文件是否存在.* @param minioMd5Value* @return*/public static boolean isExist(MinioBucketEnum minioBucketEnum, String minioMd5Value) {MinioRecord byMd5Val = minioService.getByMd5Val(minioBucketEnum.getBucketName(),minioMd5Value);if(byMd5Val==null){return false;}else{return true;}}/*** 返回minio数据库表的记录* @return*/public static MinioRecord getMinioRecordByMd5Value(MinioBucketEnum minioBucketEnum, String minioMd5Value){return minioService.getByMd5Val(minioBucketEnum.getBucketName(),minioMd5Value);}}
package com.sun.minio.util;import org.apache.commons.lang3.StringUtils;public enum MinIoContentType {PDF("application/pdf"),STREAM("application/octet-stream"),IMAGE("image/jpeg"),VEDIO("video/mp4"),TEXT("text/html");public String contentType;private MinIoContentType(String contentType){this.contentType = contentType;}public static MinIoContentType getContentType(String contentType){for(MinIoContentType minIoContentType : MinIoContentType.values()){if(minIoContentType.contentType.equals(contentType)){return minIoContentType;}}return STREAM;}public static MinIoContentType getContentTypeByFileName(String fileName){if(StringUtils.isBlank(fileName)){return STREAM;}String substring = fileName.substring(fileName.lastIndexOf("."), fileName.length());if(StringUtils.isBlank(substring)){return STREAM;}substring = substring.toLowerCase();if("pdf".equals(substring)){return PDF;}if("png".equals(substring)||"jpg".equals(substring)||"jpng".equals(substring)||"gif".equals(substring)){return IMAGE;}if("mp4".equals(substring)||"avi".equals(substring)||"mkv".equals(substring)||"mov".equals(substring)||"rmvb".equals(substring)||"FLV".equals(substring)||"rmvb".equals(substring)||"rm".equals(substring)){return VEDIO;}if("txt".equals(substring)){return TEXT;}return STREAM;}
}
package com.sun.minio.util;public enum MinioBucketEnum {//EMAIL("email"),//EXCEL("excel"),//TYPICAL("typical"),//FIRE_CONTROL("firecontrol"),//MONTHLY_TEXT("monthlytext");VIDEO_FILES("movie");private String bucketName;private MinioBucketEnum(String bucketName){this.bucketName = bucketName;}public String getBucketName(){return bucketName;}
}
package com.sun.minio.util;import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.security.MessageDigest;
/*** 网上copy的代码,获取文件的32位MD5值*/
public class MD5Util {/*** 保证文件的MD5值为32位* @param filePath	文件路径* @return* @throws FileNotFoundException*/public static String md5HashCode32(String filePath) throws Exception{BufferedInputStream bfis = new BufferedInputStream(new FileInputStream(filePath));return md5HashCode32(bfis);}/*** java计算文件32位md5值* @param fis 输入流* @return*/public static String md5HashCode32(InputStream fis) {try {fis.mark(Integer.MAX_VALUE);//拿到一个MD5转换器,如果想使用SHA-1或SHA-256,则传入SHA-1,SHA-256MessageDigest md = MessageDigest.getInstance("MD5");//分多次将一个文件读入,对于大型文件而言,比较推荐这种方式,占用内存比较少。byte[] buffer = new byte[1024];int length = -1;while ((length = fis.read(buffer, 0, 1024)) != -1) {md.update(buffer, 0, length);}fis.reset();//转换并返回包含16个元素字节数组,返回数值范围为-128到127byte[] md5Bytes  = md.digest();StringBuffer hexValue = new StringBuffer();for (int i = 0; i < md5Bytes.length; i++) {int val = ((int) md5Bytes[i]) & 0xff;//解释参见最下方if (val < 16) {/*** 如果小于16,那么val值的16进制形式必然为一位,* 因为十进制0,1...9,10,11,12,13,14,15 对应的 16进制为 0,1...9,a,b,c,d,e,f;* 此处高位补0*/hexValue.append("0");}//这里借助了Integer类的方法实现16进制的转换hexValue.append(Integer.toHexString(val));}return hexValue.toString();} catch (Exception e) {e.printStackTrace();return "";}}/***字符串经过md5加密成32位*/public static String md5HashCode32Str(String plainText) {try {MessageDigest md = MessageDigest.getInstance("MD5");md.update(plainText.getBytes());byte[] md5Bytes  = md.digest();StringBuffer hexValue = new StringBuffer();for (int i = 0; i < md5Bytes.length; i++) {int val = ((int) md5Bytes[i]) & 0xff;//解释参见最下方if (val < 16) {/*** 如果小于16,那么val值的16进制形式必然为一位,* 因为十进制0,1...9,10,11,12,13,14,15 对应的 16进制为 0,1...9,a,b,c,d,e,f;* 此处高位补0*/hexValue.append("0");}//这里借助了Integer类的方法实现16进制的转换hexValue.append(Integer.toHexString(val));}return hexValue.toString();} catch (Exception e) {e.printStackTrace();return "";}}
}
package com.sun.minio.util;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;public class DownloadUtils {private static final String JSON_APPLICATION = "application/json";private static final Logger log = LoggerFactory.getLogger(DownloadUtils.class.getName());public static boolean downloadFile( HttpServletResponse response, InputStream inputStream, String objectName, long fileLen, String contentType, boolean closeInputStream, long maxAge,String exportFileName) throws Exception {String fileName = StringUtils.isEmpty(exportFileName)?objectName.substring(objectName.lastIndexOf("/")+1):exportFileName;if (!StringUtils.isEmpty(contentType)) {response.setContentType(contentType);} else {response.setContentType("application/octet-stream");}response.setCharacterEncoding("utf-8");response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));response.setHeader("X-Actual-Content-Length", String.valueOf(fileLen));cors(response, maxAge);OutputStream out = response.getOutputStream();byte[] buffer = new byte[1024];int len;try {while ((len = inputStream.read(buffer)) != -1) {out.write(buffer, 0, len);}} catch (Exception e) {log.info("download file error.e=" + e);return false;} finally {if (closeInputStream) {inputStream.close();}out.flush();out.close();}return true;}public static void cors(HttpServletResponse response, long maxAge) {response.setHeader("Access-Control-Allow-Origin", "*");response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, HEAD");if (maxAge > 0) {response.setHeader("Access-Control-Max-Age", String.valueOf(maxAge));response.setHeader("Cache-Control", "max-age=" + maxAge);}response.setHeader("Access-Control-Allow-Headers", "*");response.setHeader("Access-Control-Expose-Headers", "Cache-Control, Accept-Ranges, Content-Encoding, Content-Length, Content-Range, X-Actual-Content-Length, Content-Disposition");}
}

在这里插入图片描述

相关文章:

Java工程师实现视频文件上传minio文件系统存储及网页实现分批加载视频播放

Java工程师实现minio存储大型视频文件网页实现分批加载视频播放 一、需求说明 老板给我出个题目&#xff0c;让我把的电影文件上传到minio文件系统&#xff0c;再通过WEB端分配加载视频播放&#xff0c;类似于我们普通的电影网站。小编把Java代码共享出来。是真正的能拿过来直…...

html5css3

1.html5新增语义化标签 <header><nav><article><section><aside><footer> 2.新增多媒体标签 视频<video>格式&#xff1a;map4,webm,ogg <video controls"controls" autoplay"autoplay" muted"mute…...

运维人员的Go语言学习路线

以下是一份更为详细的适合运维人员的Go语言学习路线图&#xff1a; 一、基础环境搭建与入门&#xff08;第 1 - 2 周&#xff09; 第 1 周 环境搭建 在本地开发机和常用的运维服务器环境&#xff08;如 Linux 系统&#xff09;中安装 Go 语言。从官方网站&#xff08;https://…...

怎么在家访问公司服务器?

在日常工作中&#xff0c;特别是对信息技术从业者而言&#xff0c;工作往往离不开公司的服务器。他们需要定期访问服务器&#xff0c;获取一些关键的机密文件或数据。如果您在家办公&#xff0c;并且需要处理未完成的任务&#xff0c;同时需要从公司服务器获取所需的数据&#…...

【linux学习指南】】Ext系列文件系统(三)ext2 文件系统的认识与构成

文章目录 &#x1f4dd;ext2 ⽂件系统&#x1f320; 宏观认识&#x1f309; Block Group &#x1f320;块组内部构成&#x1f309;超级块&#xff08;SuperBlock&#xff09; &#x1f320;GDT&#xff08;GroupDescriptorTable&#xff09;&#x1f309;块位图&#xff08;Blo…...

区块链安全常见的攻击分析——Unprotected callback - ERC721 SafeMint reentrancy【8】

区块链安全常见的攻击分析——Unprotected callback - ERC721 SafeMint reentrancy【8】 1.1 漏洞分析1.2 漏洞合约1.3 攻击分析1.4 攻击合约 重点&#xff1a;MaxMint721 漏洞合约的 mint 函数调用了 ERC721 合约中的 _checkOnERC721Received 函数&#xff0c;触发 to 地址中实…...

Linux中sed命令的使用技巧

一、sed语法介绍 sed命令主要用于文本内容的编辑&#xff0c;默认只处理模式空间&#xff0c;不处理原数据。 命令格式&#xff1a; sed [option] command filename示例&#xff1a;删除空白行 sed ‘/^\s*$/d’ filename option 参数&#xff1a; -n&#xff1a;只有经过sed…...

小米路由器开启SSH,配置阿里云ddns,开启外网访问SSH和WEB管理界面

文章目录 前言一、开启SSH二、配置阿里云ddns1.准备工作2.创建ddns脚本3.添加定时任务 三、开启外网访问SSH和WEB管理界面1、解除WEB管理页面访问限制2.手动添加防火墙端口转发规则&#xff0c;开启外网访问WEB管理和SSH 前言 例如&#xff1a;随着人工智能的不断发展&#xf…...

Flink源码解析之:如何根据JobGraph生成ExecutionGraph

Flink源码解析之&#xff1a;如何根据JobGraph生成ExecutionGraph 在上一篇Flink源码解析中&#xff0c;我们介绍了Flink如何根据StreamGraph生成JobGraph的流程&#xff0c;并着重分析了其算子链的合并过程和JobGraph的构造流程。 对于StreamGraph和JobGraph的生成来说&…...

活动预告 |【Part2】Microsoft 安全在线技术公开课:安全性、合规性和身份基础知识

课程介绍 通过参加“Microsoft 安全在线技术公开课&#xff1a;安全性、合规性和身份基础知识”活动提升你的技能。在本次免费的介绍性活动中&#xff0c;你将获得所需的安全技能和培训&#xff0c;以创造影响力并利用机会推动职业发展。你将了解安全性、合规性和身份的基础知…...

网络基础入门到深入(2):网络协议-TCP/IP协议栈

目录 一.TCP/IP协议栈的四层结构 二.每一层的作用与协议 1.作用层 作用&#xff1a; 常见协议: 示例: 2.传输层 作用: 核心功能: 3.网络层 作用: 核心功能: 常见协议: 示例: 4.数据链路层(物理层) 作用: 核心功能: 常见技术: 示例: 三.TCP/IP协议栈的分层…...

美畅物联丨视频上云网关获取视频流地址供第三方调用的方法

在视频监控与流媒体传输领域&#xff0c;视频流地址的获取与调用是极为关键的环节。视频上云网关作为一款高效且稳定的视频传输设备&#xff0c;为获取视频流地址提供了便捷途径&#xff0c;从而使外部系统或平台能够方便地进行调用。今天我们就来讨论一下如何在视频上云网关上…...

【Cesium】一、cesium简介

文章目录 前言1.什么是Cesium&#xff1f;2.Cesium能做什么&#xff1f;3.Cesium的依赖性4.Cesium学习参考 前言 本人是前端&#xff0c;主要是开发web&#xff0c;使用技术栈Vue、Js。最近因工作需要开始学习使用Cesium&#xff0c;找到一位博主的文章很好&#xff0c;一边学…...

微服务架构介绍

微服务架构是一种现代化的软件架构风格&#xff0c;它将应用程序构建为一组小型、自治的服务&#xff0c;每个服务都运行在其独立的进程中&#xff0c;服务与服务之间通过轻量级通信机制&#xff08;通常是HTTP/RESTful API&#xff09;进行通信。 1. 服务&#xff08;Service&…...

SOLID-开闭原则

单一职责原则&#xff1a;https://blog.csdn.net/dmk877/article/details/143447010 在前面我们学习了单一职责原则&#xff0c;今天来一起学习一下SOLID原则中的开闭原则(Open-Closed Principle, OCP) 通过本篇博客你将学到到以下内容 ①什么是开闭原则 ②如何实现开闭原则 ③…...

Mac 安装 Flutter 提示 A network error occurred while checking

错误信息 A network error occurred while checking "https://maven.google.com/": Operation timed out原因 在中国大陆(由于访问 Google 服务器的限制导致超时),无法连接到 https://maven.google.com/ 解决方案 需要使用镜像网站 #flutter 使用国内的镜像 export …...

Rocky Linux下安装meld

背景介绍&#xff1a; meld是一款Linux系统下的用于 文件夹和文件的比对软件&#xff0c;非常常用&#xff1b; 故障现象&#xff1a; 输入安装命令后&#xff0c;sudo yum install meld&#xff0c;报错。 12-31 22:12:17 ~]$ sudo yum install meld Last metadata expirat…...

Sentinel 介绍与使用指南:构建高可用、可靠的微服务架构

在微服务架构中&#xff0c;服务间的依赖和调用非常复杂&#xff0c;这也带来了高并发、大流量等挑战。 如何确保系统在高负载情况下仍能稳定运行&#xff0c;如何避免某个服务的故障影响整个系统的稳定性&#xff1f;Sentinel&#xff0c;作为一个轻量级的、专为分布式系统设计…...

异步请求在TypeScript网络爬虫中的应用

异步请求的重要性 异步请求是现代网络应用中不可或缺的一部分&#xff0c;特别是在网络爬虫领域。它允许爬虫在等待网络响应的同时继续执行其他任务&#xff0c;从而提高效率和性能。在JavaScript和TypeScript中&#xff0c;异步请求可以通过多种方式实现&#xff0c;包括回调…...

智能商业分析 Quick BI

Quick BI 是阿里云提供的一款智能商业分析&#xff08;BI&#xff09;工具&#xff0c;旨在帮助企业快速获取业务洞察、优化决策过程、提升数据分析效率。通过强大的数据可视化和分析功能&#xff0c;Quick BI 能够帮助用户轻松连接多种数据源、创建多维度的报表和仪表盘&#…...

[算法] [leetcode-75] 颜色分类

75 颜色分类 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums &#xff0c;原地 对它们进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;并按照红色、白色、蓝色顺序排列。 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 必须在不使用库内置的 sort 函…...

抖音短视频矩阵系统源码开发技术解析

开发概览&#xff1a; 抖音短视频矩阵系统的构建基于一系列现代技术栈&#xff0c;主要包括VUE, Spring Boot和Django。本文档旨在为开发者提供关于短视频矩阵系统源代码的开发与部署指南。 技术框架分析&#xff1a; 前端技术选型&#xff1a; 对于前端界面的构建&#xf…...

Linux(CentOS)安装 MySQL

CentOS版本&#xff1a;CentOS 7 三种安装方式&#xff1a; 一、通过 yum 安装&#xff0c;最简单&#xff0c;一键安装&#xff0c;全程无忧。 二、通过 rpm 包安装&#xff0c;需具备基础概念及常规操作。 三、通过 gz 包安装&#xff0c;需具备配置相关操作。 --------…...

头歌实训数据结构与算法-二叉树及其应用(第9关:二叉树的顺序存储及基本操作)

任务描述 本关任务&#xff1a;以顺序结构存储二叉树&#xff0c;编写前序、中序、后序及层次顺序遍历二叉树的算法&#xff0c;并计算二叉树深度、所有结点总数。 相关知识 二叉树的定义 二叉树的递归定义&#xff1a; 二叉树或者是一棵空树。 或者是一棵由一个根结点和两…...

打印进度条

文章目录 1.Python语言实现(1)黑白色(2)彩色&#xff1a;蓝色 2.C语言实现(1)黑白颜色(2)彩色版&#xff1a;红绿色 1.Python语言实现 (1)黑白色 import sys import timedef progress_bar(percentage, width50):"""打印进度条:param percentage: 当前进度百分比…...

【LLM】Langflow 的简单使用

(PS&#xff1a;爆肝整理&#xff0c;请不要吝啬你的点赞和收藏。) 什么是 Langflow &#xff1f;Langflow 是一种用于构建多智能体和RAG应用的可视化框架。它提供了个无需编码的 AI 生态系统&#xff0c;能够无缝集成各种常用工具和技术栈。Langflow 以 Python 为基础&#x…...

探索 DC-SDK:强大的 3D 地图开发框架

在现代 Web 开发中&#xff0c;地理信息系统&#xff08;GIS&#xff09;和 3D 地图可视化变得越来越重要。dc-sdk 是一个基于 Cesium 的开源 WebGL 地图开发框架&#xff0c;它提供了丰富的地图可视化功能和简单易用的 API&#xff0c;使开发者能够轻松地在 Web 应用中集成 3D…...

3.5mm耳机接口硬件连接

结构 以最复杂的结构为例 简单的结构无非就是没有MIC&#xff08;麦克风&#xff09;接口 上图的5就是Detect的作用 上面这两款产品都为3.5mm的音频插座&#xff0c;图一 为连接4节的音频座&#xff0c;而且有两个开关&#xff0c;1接地&#xff0c;2接MIC&#xff0c;3接左声…...

nvidia_gpu_exporter 显卡监控

导入 grafana/dashboard.json https://github.com/utkuozdemir/nvidia_gpu_exporter/blob/master/grafana/dashboard.json参考 nvidia_gpu_exporter...

聊聊 Mongod 以及 MongoDB 常用命令

Mongod mongod 是 MongoDB 数据库服务器的核心守护进程&#xff0c;它负责启动并管理 MongoDB 数据库实例。简单来说&#xff0c;mongod 是 MongoDB 数据库服务器程序&#xff0c;它负责处理数据存储、数据请求、数据复制等后台服务。运行 mongod 是启动 MongoDB 数据库的第一…...

webrtc 源码阅读 make_ref_counted模板函数用法

目录 1. 模板参数解析 1.1 typename T 1.2 typename... Args 1.3 typename std::enable_if::value, T>::type* nullptr 2. scoped_refptr 3. new RefCountedObject(std::forward(args)...); 4. 综合说明 5.在webrtc中的用法 5.1 peerConnectionFactory对象的构建过…...

僵尸进程,孤儿进程、守护进程以及wait函数,waitpid函数

僵尸进程 如果子进程退出&#xff0c;但是父进程没有调用 wait 或 waitpid 获取子进程的状态信息&#xff0c;那么子进程的进程描述符&#xff08;task_struct&#xff09;仍然保存在系统中&#xff0c;那么该子进程叫做僵尸进程 #include<iostream> #include<pthre…...

Kafka消息不丢失与重复消费问题解决方案总结

1. 生产者层面 异步发送与回调处理 异步发送方式&#xff1a;生产者一般使用异步方式发送消息&#xff0c;异步发送有消息和回调接口两个参数。在回调接口的重写方法中&#xff0c;可通过异常参数判断消息发送状态。若消息发送成功&#xff0c;异常参数为null&#xff1b;若发…...

Docker新手:在tencent云上实现Python服务打包到容器

1 使用docker的原因 一致性和可移植性&#xff1a;Docker 容器可以在任何支持 Docker 的环境中运行&#xff0c;无论是开发者的笔记本电脑、测试服务器还是生产环境。这确保了应用在不同环境中的行为一致&#xff0c;减少了“在我的机器上可以运行”的问题。 隔离性&#xff…...

什么是 Spring 的组件(Bean)

什么是 Spring 的组件&#xff08;Bean&#xff09;&#xff1f; Spring 会自动创建、初始化、装配和销毁这些对象。Spring 使用 IoC&#xff08;控制反转&#xff09; 和 DI&#xff08;依赖注入&#xff09; 的理念&#xff0c;将应用程序的对象交给 Spring 容器统一管理&am…...

PawSQL性能巡检平台 (3) - 慢查询采集和优化

在数据库运维管理中&#xff0c;慢查询一直是影响系统性能的重要因素。本文将详细介绍PawSQL数据库性能巡检平台在慢查询管理和优化方面的功能特性&#xff0c;帮助数据库管理员更好地应对性能挑战。 一、PawSQL巡检平台慢查询管理概述 PawSQL平台提供了全面的慢查询管理功能&…...

虚拟机Centos下安装Mysql完整过程(图文详解)

目录 一. 准备工作 1. 设置虚拟机静态IP 2. 卸载Mysql 3. 给CentOS添加rpm源 二. 安装MySQL 1. 安装mysql服务 2. 启动mysql服务 3. 开启MySQL开机自启动 4. 查看mysql服务状态 5. 查看mysql初始密码 6. 登录mysql &#xff0c;修改密码 7. 允许外部访问MySQL数据库…...

微服务保护-sentinel

为什么要有微服务保护&#xff1f; 微服务保护是为了避免微服务雪崩而出现的&#xff0c;每个微服务能处理的请求是有限的&#xff0c;如果一个微服务出现问题导致一个请求进入微服务的时间太久&#xff0c;就会导致大量去请求停滞在微服务内部&#xff0c;这样就会过分占用系统…...

Redis Java 集成到 Spring Boot

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;Redis &#x1f4da;本系列文章为个人学习笔…...

RabbitMQ实现生产者消费者

一.启动MQ 注意管理员身份进入cmd才行,我这里是在本地安装的MQ,推荐使用虚拟机安装 二.思路 官方解释RabbitMQ结构: 自我理解RabbitMQ结构: 其实RabbitMQ的服务器就像邮局一样,我们的生产者和消费者对于这个服务器来说都是消费者,因为服务器都可以向两者发送消息 环境准备 …...

stm32f103zet6 ds18b20

main.c // main.c #include "sys.h" #include "ds18b20.h"int main(void){ uart_init(9600);delay_init();while(DS18B20_Init()) //DS18B20初始化 {printf("error");delay_ms(200);}while(1){printf("%4.2f\r\n",Get_Temp());}}ds18…...

期权懂|期权入门知识:开通50ETF期权需要什么条件?

锦鲤三三每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 开通50ETF期权需要什么条件&#xff1f; 一、基本资格要求 &#xff08;1&#xff09;年龄限制&#xff1a;投资者必须年满18周岁&#xff0c;具备完全民事行为能力。 &#…...

Linux day 1129

家人们今天继续学习Linux&#xff0c;ok话不多说一起去看看吧 三.Linux常用命令 3.1 Linux命令体验 3.1.1 常用命令演示 在这一部分中&#xff0c;我们主要介绍几个常用的命令&#xff0c;让大家快速感 受以下 Linux 指令的操作方式。主要包含以下几个指令&#xff1a; ls命…...

智能家居体验大变革 博联 AI 方案让智能不再繁琐

1. 全球AI技术发展背景及智能家居市场趋势 人工智能&#xff08;AI&#xff09;技术的飞速发展正在推动全球各行业的数字化转型。国际电信联盟与德勤联合发布《人工智能向善影响》报告指出&#xff0c;全球94%的商界领袖认为&#xff0c;人工智能技术对于其企业在未来5年内的发…...

git使用

git初始化 git init 指定要添加的文件 git add [文件名1] [文件名2] [文件名3] // 添加指定文件 git add . // 添加当前目录所有文件 将文件提交到本地仓库 git commit -m "备注信息" 添加远程仓库 git remote add origin [远程仓库地址] git remote -v // …...

嵌入科技的温情

嵌入式世界&#xff0c;是一个微小却无比精妙的宇宙。晶体管之间的脉动&#xff0c;仿佛是心跳的回响&#xff1b;代码中跳跃的逻辑&#xff0c;犹如人生中不可预知的转折。每一个嵌入式系统&#xff0c;都像是一个看不见的灵魂&#xff0c;将冰冷的机器唤醒&#xff0c;为生活…...

python利用selenium实现大麦网抢票

大麦网&#xff08;damai.cn&#xff09;是中国领先的现场娱乐票务平台&#xff0c;涵盖演唱会、音乐会、话剧、歌剧、体育赛事等多种门票销售。由于其平台上经常会有热门演出&#xff0c;抢票成为许多用户关注的焦点。然而&#xff0c;由于票务资源的有限性&#xff0c;以及大…...

PS等软件学习笔记

目录 一、ps基础操作快捷键 1、快速打开图片 2、屏幕画布变大变小 3、移动画布 4、CTRL回车&#xff0c;快速完成更改 5、还原 6、创建画布&#xff0c;CTRLN 7、复制图层&#xff0c;CTRLJ 8、一段文字行间距调整 9、反向选择&#xff0c;CTRLSHIFTI 10、抠图 二、…...

vue3学习笔记(9)-pinia、storeToRefs、getters

1.新的集中式状态&#xff08;数据&#xff09;管理库&#xff0c;redux vuex pinia 搭建 2.ref拆包 如果在reactive里面定义ref&#xff0c;则打印c时&#xff0c;无需.value 他自动拆包&#xff0c;如果直接在外面定义的ref则需要.value,他没有拆包 3.pinia存储读取数据 存…...

数据库基础知识---以MySQL为例

一、什么是MySQL 数据保存在不同的表中&#xff0c;而不是将所有数据放在一个大仓库内 二、特点 开源--免费下载跨平台--可以在多个操作系统进行运行性能好--可以出来大量数据简单--安装配置简单支持多种编程语言--可以与多种编程语言进行无缝集成 三、分类 DDL--数据定义…...