(十六)admin-boot项目之文件存储上传与下载minio
(十六)文件存储上传与下载
项目地址:https://gitee.com/springzb/admin-boot
如果觉得不错,给个 star
简介:
这是一个基础的企业级基础后端脚手架项目,主要由springboot为基础搭建,后期整合一些基础插件例如:redis、xxl-job、flowable、minioio、easyexcel、skyWalking、rabbitmq
文章目录
- (十六)文件存储上传与下载
- 一、简介
- 二、编码设计
- OssProperties 配置
- OssTemplate 所有存储系统的顶级类
- MinioTemplate 具体存储系统的实现
- Oss全局配置
- OssException 自定义异常
- GlobalExceptionHandler 全局拦截处理Oss异常
- Oss模块结构
- 三、测试与使用
- 四、文件下载
一、简介
此处采用分布式存储系统,这里以minio为例
官方文档地址
https://docs.min.io/docs/java-client-quickstart-guide.html
二、编码设计
OssProperties 配置
package cn.mesmile.admin.common.oss;import cn.mesmile.admin.common.oss.enums.OssTypeEnum;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;/*** @author zb* @Description oss 配置*/
@Data
@ConfigurationProperties(prefix = "oss")
public class OssProperties {/*** 是否启用 Oss 文件存储*/private Boolean enabled = Boolean.FALSE;/*** 文件存储系统类型*/private OssTypeEnum type;/*** oss对外开放的地址*/private String endpoint;/*** accessKey*/private String accessKey;/*** secretKey*/private String secretKey;/*** 桶名称*/private String bucketName = "resource";
}
OssTemplate 所有存储系统的顶级类
package cn.mesmile.admin.common.oss.template;import cn.mesmile.admin.common.oss.domain.AdminFile;
import cn.mesmile.admin.common.oss.domain.OssFile;
import org.springframework.web.multipart.MultipartFile;import java.io.InputStream;
import java.util.List;/*** Oss操作模板* @author zb* @Description Oss统一操作方法*/
public interface OssTemplate {/*** 创建桶* @param bucketName 桶名称*/void makeBucket(String bucketName);/*** 根据桶名称 删除桶* @param bucketName 桶名称*/void removeBucket(String bucketName);/*** 根据桶名称,判断桶是否存在* @param bucketName 桶名称* @return 是否存在*/boolean bucketExists(String bucketName);/*** 把一个桶里面的文件,拷贝到另外一个桶* @param bucketName 源桶名称* @param fileName 文件名* @param destBucketName 目标桶名称*/void copyFile(String bucketName, String fileName, String destBucketName);/*** 把一个桶里面的文件,拷贝到另外一个桶* @param bucketName 源桶名称* @param fileName 文件名* @param destBucketName 目标桶名称* @param destFileName 目标文件名*/void copyFile(String bucketName, String fileName, String destBucketName, String destFileName);/*** 统计文件* @param fileName 文件名* @return 文件相关信息*/OssFile statFile(String fileName);/*** 统计文件* @param bucketName 桶名称* @param fileName 文件名* @return 文件相关信息*/OssFile statFile(String bucketName, String fileName);/*** 获取文件路径* @param fileName 文件名* @return 文件路径*/String filePath(String fileName);/*** 获取文件路径* @param bucketName 桶名称* @param fileName 文件名* @return 文件路径*/String filePath(String bucketName, String fileName);/*** 获取文件下载链接* @param fileName 文件名* @return 文件下载链接*/String fileLink(String fileName);/*** 获取文件下载链接* @param bucketName 桶名称* @param originalFilename 文件名* @return 文件下载链接*/String fileLink(String bucketName, String originalFilename);/*** 上传文件* @param file 文件* @return 上传结果*/AdminFile putFile(MultipartFile file);/*** 上传文件* @param originalFilename 文件名称* @param file 文件* @return 上传结果*/AdminFile putFile(String originalFilename, MultipartFile file);/*** 上传文件* @param bucketName 桶名称* @param originalFilename 文件名称* @param file 文件* @return 上传结果*/AdminFile putFile(String bucketName, String originalFilename, MultipartFile file);/*** 上传文件* @param originalFilename 文件名称* @param stream 输入流* @return 上传结果*/AdminFile putFile(String originalFilename, InputStream stream);/*** 上传文件* @param bucketName 桶名称* @param originalFilename 文件名称* @param stream 输入流* @return 上传结果*/AdminFile putFile(String bucketName, String originalFilename, InputStream stream);/*** 删除文件* @param originalFilename 文件名称*/void removeFile(String originalFilename);/*** 删除文件* @param bucketName 桶名称* @param fileName 文件名称*/void removeFile(String bucketName, String fileName);/*** 删除一个或多个文件* @param fileNames 一个或多个文件名称*/void removeFiles(List<String> fileNames);/*** 删除文件* @param bucketName 桶名称* @param fileNames 一个或多个文件名称*/void removeFiles(String bucketName, List<String> fileNames);
}
MinioTemplate 具体存储系统的实现
package cn.mesmile.admin.common.oss.template;import cn.hutool.core.util.StrUtil;
import cn.mesmile.admin.common.exceptions.OssException;
import cn.mesmile.admin.common.oss.OssProperties;
import cn.mesmile.admin.common.oss.domain.AdminFile;
import cn.mesmile.admin.common.oss.domain.OssFile;
import cn.mesmile.admin.common.oss.enums.PolicyTypeEnum;
import cn.mesmile.admin.common.oss.rule.OssRule;
import cn.mesmile.admin.common.result.ResultCode;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;import java.awt.*;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;/*** @author zb* @Description mini操作模板*/
public class MinioTemplate implements OssTemplate {private final OssRule ossRule;private final MinioClient minioClient;private final OssProperties ossProperties;public MinioTemplate(OssRule ossRule, MinioClient minioClient,OssProperties ossProperties) {this.ossRule = ossRule;this.minioClient = minioClient;this.ossProperties = ossProperties;}@Overridepublic void makeBucket(String bucketName) {try {BucketExistsArgs build = BucketExistsArgs.builder().bucket(bucketName).build();if (!minioClient.bucketExists(build)) {MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();minioClient.makeBucket(makeBucketArgs);String policyTypeEnum = getPolicyTypeEnum(bucketName, PolicyTypeEnum.READ);minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policyTypeEnum).build());}} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}public Bucket getBucket() {return getBucket(ossProperties.getBucketName());}public Bucket getBucket(String bucketName) {try {Optional<Bucket> bucketOptional = minioClient.listBuckets().stream().filter((bucket) -> {return bucket.name().equals(bucketName);}).findFirst();return bucketOptional.orElse(null);} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}public List<Bucket> listBuckets() {try {return minioClient.listBuckets();} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}@Overridepublic void removeBucket(String bucketName) {try {minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}@Overridepublic boolean bucketExists(String bucketName) {try {BucketExistsArgs build = BucketExistsArgs.builder().bucket(bucketName).build();return minioClient.bucketExists(build);} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}@Overridepublic void copyFile(String bucketName, String fileName, String destBucketName) {try {this.copyFile(bucketName, fileName, destBucketName, fileName);} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}@Overridepublic void copyFile(String bucketName, String fileName, String destBucketName, String destFileName) {try {CopySource copySource = CopySource.builder().bucket(bucketName).object(fileName).build();CopyObjectArgs copyObjectArgs = CopyObjectArgs.builder().source(copySource).bucket(destBucketName).object(destFileName).build();minioClient.copyObject(copyObjectArgs);} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}@Overridepublic OssFile statFile(String fileName) {try {return statFile(ossProperties.getBucketName(), fileName);} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}@Overridepublic OssFile statFile(String bucketName, String fileName) {try {StatObjectResponse stat = minioClient.statObject(((StatObjectArgs.builder().bucket(bucketName)).object(fileName)).build());OssFile ossFile = new OssFile();ossFile.setName(StrUtil.isEmpty(stat.object()) ? fileName : stat.object());ossFile.setUrl(fileLink(ossFile.getName()));ossFile.setHash(String.valueOf(stat.hashCode()));ossFile.setLength(stat.size());LocalDateTime localDateTime = stat.lastModified().toLocalDateTime();ossFile.setPutTime(localDateTime);ossFile.setContentType(stat.contentType());return ossFile;} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}@Overridepublic String filePath(String fileName) {return ossProperties.getBucketName().concat("/").concat(fileName);}@Overridepublic String filePath(String bucketName, String fileName) {return bucketName.concat("/").concat(fileName);}@Overridepublic String fileLink(String fileName) {return ossProperties.getEndpoint().concat("/").concat(ossProperties.getBucketName()).concat("/").concat(fileName);}@Overridepublic String fileLink(String bucketName, String originalFilename) {return ossProperties.getEndpoint().concat("/").concat(ossProperties.getBucketName()).concat("/").concat(originalFilename);}@Overridepublic AdminFile putFile(MultipartFile file) {return putFile(ossProperties.getBucketName(), file.getOriginalFilename(), file);}@Overridepublic AdminFile putFile(String fileName, MultipartFile file) {return putFile(ossProperties.getBucketName(), fileName, file);}@Overridepublic AdminFile putFile(String bucketName, String originalFilename, MultipartFile file) {try {return putFile(bucketName, file.getOriginalFilename(), file.getInputStream());} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}@Overridepublic AdminFile putFile(String originalFilename, InputStream stream) {return putFile(ossProperties.getBucketName(), originalFilename, stream);}@Overridepublic AdminFile putFile(String bucketName, String originalFilename, InputStream stream) {return putFile(bucketName, originalFilename, stream, MediaType.APPLICATION_OCTET_STREAM_VALUE);}public AdminFile putFile(String bucketName, String originalFilename, InputStream stream, String contentType) {try {makeBucket(bucketName);String fileName = getFileName(originalFilename);PutObjectArgs putObjectArgs = PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(stream, stream.available(), -1L).contentType(contentType).build();minioClient.putObject(putObjectArgs);return new AdminFile(fileLink(bucketName, fileName), getOssHost(bucketName), fileName, originalFilename);} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}@Overridepublic void removeFile(String fileName) {removeFile(ossProperties.getBucketName(), fileName);}@Overridepublic void removeFile(String bucketName, String fileName) {try {RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build();minioClient.removeObject(removeObjectArgs);} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}@Overridepublic void removeFiles(List<String> fileNames) {removeFiles(ossProperties.getBucketName(), fileNames);}@Overridepublic void removeFiles(String bucketName, List<String> fileNames) {try {Stream<DeleteObject> stream = fileNames.stream().map(DeleteObject::new);RemoveObjectsArgs removeObjectsArgs = RemoveObjectsArgs.builder().bucket(bucketName).objects(stream::iterator).build();minioClient.removeObjects(removeObjectsArgs);} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}private String getFileName(String originalFilename) {return ossRule.setName(originalFilename);}public String getPresignedObjectUrl(String bucketName, String fileName, Integer expires) {try {GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(fileName).expiry(expires).build();return minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs);} catch (Exception e) {throw new OssException(ResultCode.FAILURE, "minio异常", e);}}public String getPolicyTypeEnum(PolicyTypeEnum policyType) {return getPolicyTypeEnum(ossProperties.getBucketName(), policyType);}public static String getPolicyTypeEnum(String bucketName, PolicyTypeEnum policyType) {StringBuilder builder = new StringBuilder();builder.append("{\n");builder.append(" \"Statement\": [\n");builder.append(" {\n");builder.append(" \"Action\": [\n");switch(policyType) {case WRITE:builder.append(" \"s3:GetBucketLocation\",\n");builder.append(" \"s3:ListBucketMultipartUploads\"\n");break;case READ_WRITE:builder.append(" \"s3:GetBucketLocation\",\n");builder.append(" \"s3:ListBucket\",\n");builder.append(" \"s3:ListBucketMultipartUploads\"\n");break;default:builder.append(" \"s3:GetBucketLocation\"\n");}builder.append(" ],\n");builder.append(" \"Effect\": \"Allow\",\n");builder.append(" \"Principal\": \"*\",\n");builder.append(" \"Resource\": \"arn:aws:s3:::");builder.append(bucketName);builder.append("\"\n");builder.append(" },\n");if (PolicyTypeEnum.READ.equals(policyType)) {builder.append(" {\n");builder.append(" \"Action\": [\n");builder.append(" \"s3:ListBucket\"\n");builder.append(" ],\n");builder.append(" \"Effect\": \"Deny\",\n");builder.append(" \"Principal\": \"*\",\n");builder.append(" \"Resource\": \"arn:aws:s3:::");builder.append(bucketName);builder.append("\"\n");builder.append(" },\n");}builder.append(" {\n");builder.append(" \"Action\": ");switch(policyType) {case WRITE:builder.append("[\n");builder.append(" \"s3:AbortMultipartUpload\",\n");builder.append(" \"s3:DeleteObject\",\n");builder.append(" \"s3:ListMultipartUploadParts\",\n");builder.append(" \"s3:PutObject\"\n");builder.append(" ],\n");break;case READ_WRITE:builder.append("[\n");builder.append(" \"s3:AbortMultipartUpload\",\n");builder.append(" \"s3:DeleteObject\",\n");builder.append(" \"s3:GetObject\",\n");builder.append(" \"s3:ListMultipartUploadParts\",\n");builder.append(" \"s3:PutObject\"\n");builder.append(" ],\n");break;default:builder.append("\"s3:GetObject\",\n");}builder.append(" \"Effect\": \"Allow\",\n");builder.append(" \"Principal\": \"*\",\n");builder.append(" \"Resource\": \"arn:aws:s3:::");builder.append(bucketName);builder.append("/*\"\n");builder.append(" }\n");builder.append(" ],\n");builder.append(" \"Version\": \"2012-10-17\"\n");builder.append("}\n");return builder.toString();}public String getOssHost(String bucketName) {return ossProperties.getEndpoint() + "/" + bucketName;}public String getOssHost() {return getOssHost(ossProperties.getBucketName());}}
Oss全局配置
package cn.mesmile.admin.common.oss;import cn.mesmile.admin.common.exceptions.OssException;
import cn.mesmile.admin.common.exceptions.ServiceException;
import cn.mesmile.admin.common.oss.enums.OssTypeEnum;
import cn.mesmile.admin.common.oss.template.MinioTemplate;
import cn.mesmile.admin.common.oss.template.OssTemplate;
import cn.mesmile.admin.common.result.ResultCode;
import cn.mesmile.admin.common.utils.SpringUtil;/*** @author zb* @Description Oss客户端操作对象统一调度*/
public class OssBuilder {private OssBuilder(){}/*** 获取 Oss 统一调度对象* @return Oss客户端操作对象*/public static OssTemplate build () {OssProperties ossProperties = SpringUtil.getBean(OssProperties.class);Boolean enabled = ossProperties.getEnabled();if (enabled == null || !enabled){throw new OssException(ResultCode.FAILURE, "Oss存储系统未开启");}OssTypeEnum type = ossProperties.getType();if (OssTypeEnum.MINIO_OSS.equals(type)){return SpringUtil.getBean(MinioTemplate.class);}else if (OssTypeEnum.ALI_OSS.equals(type)){// todo 扩展其他oss}else if (OssTypeEnum.QIANNIU_OSS.equals(type)) {}else if (OssTypeEnum.TENCENT_OSS.equals(type)){}throw new OssException(ResultCode.FAILURE, "构建oss客户端异常");}}
OssException 自定义异常
package cn.mesmile.admin.common.exceptions;import cn.mesmile.admin.common.result.IResultCode;
import cn.mesmile.admin.common.result.ResultCode;/*** @author zb* @Description oss存储服务异常*/
public class OssException extends RuntimeException {private final long serialVersionUID = 1L;private int code = ResultCode.FAILURE.getCode();private String msg = ResultCode.FAILURE.getMessage();public OssException() {super();}public OssException(String msg) {super(msg);this.msg = msg;}public OssException(IResultCode resultCode, String msg) {super(msg);this.code = resultCode.getCode();this.msg = msg;}public OssException(String msg, Throwable cause) {super(msg, cause);this.msg = msg;}public OssException(IResultCode resultCode, String msg, Throwable cause) {super(msg, cause);this.code = resultCode.getCode();this.msg = msg;}public OssException(Throwable cause) {super(cause);}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}
}
GlobalExceptionHandler 全局拦截处理Oss异常
package cn.mesmile.admin.common.handler;import cn.mesmile.admin.common.constant.AdminConstant;
import cn.mesmile.admin.common.exceptions.*;
import cn.mesmile.admin.common.result.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.stream.Collectors;/*** @author zb* @Description 全局异常拦截* <p>* 如果我同时捕获了父类和子类,那么到底能够被那个异常处理器捕获呢?比如 Exception 和 BusinessException* 当然是 BusinessException 的异常处理器捕获了,精确匹配,如果没有 BusinessException 的异常处理器才会轮到它的 父亲 ,* 父亲 没有才会到 祖父 。总之一句话, 精准匹配,找那个关系最近的* </p>*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(OssException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public R handle(OssException ossException) {// 这里记录所有堆栈信息log.error("oss异常信息, 消息:{} 编码:{}", ossException.getMessage(), ossException.getCode(), ossException);return R.fail(ossException.getCode(), ossException.getMessage());}
}
Oss模块结构
三、测试与使用
Oss统一上传方法: OssBuilder.build().putFile(file);
package cn.mesmile.admin.modules.system.controller;import cn.mesmile.admin.common.oss.OssBuilder;
import cn.mesmile.admin.common.oss.domain.AdminFile;
import cn.mesmile.admin.common.oss.template.OssTemplate;
import cn.mesmile.admin.common.result.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;/*** @author zb* @Description*/
@Api(tags = "测试接口")
@Slf4j
@RequestMapping("/api/v1/hello")
@RestController
public class HelloController {@PostMapping("/upload")public R upload(@RequestParam("file") MultipartFile file){AdminFile adminFile = OssBuilder.build().putFile(file);return R.data(adminFile);}
}
在application-dev.yaml中新增配置
# oss 相关配置
oss:enabled: truetype: minio_ossendpoint: https://play.min.ioaccess-key: Q3AM3UQ867SPQQA43P2Fsecret-key: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TGbucket-name: resource
上传成功,返回链接
四、文件下载
输入上传成功后返回的链接:
(十六)admin-boot项目之文件存储上传与下载minio相关推荐
- 瑞吉外卖项目:文件的上传与下载
目录 一. 什么是文件上传与下载 文件上传 文件下载 二. 文件上传代码实现 API请求 三. 文件下载代码实现 一. 什么是文件上传与下载 文件上传 文件上传,是指将本地图片.视频.音频等文件上传到 ...
- SpringBoot 项目将文件图片资源上传到本地静态资源文件夹下(指定文件夹下)
1.SpringBoot 项目将文件图片资源上传到本地静态资源文件夹下(指定文件夹下) 最终效果: 前端浏览本地文件,点击上传至本地resources/static/images/imgWall下 2 ...
- Python实现向s3共享存储上传和下载文件
Python实现向s3共享存储上传和下载文件 https://www.cnblogs.com/liang545621/p/10298617.html 使用Python从S3上传和下载文件 https: ...
- SpringBoot实现oss文件的上传与下载
SpringBoot实现oss文件的上传与下载 最近项目中需要通过OSS来实现文件的上传和下载以及根据oss文件(word模板)生成Word,特此记录,以便日后查阅. 一.相关概述 OSS对象存储 ...
- SpringMVC实现文件的上传和下载
SpringMVC实现文件的上传和下载http://www.bieryun.com/1120.html 前些天一位江苏经贸的学弟跟我留言问了我这样一个问题:"用什么技术来实现一般网页上文件的 ...
- Akka实战:HTTP大文件断点上传、下载,秒传
2019独角兽企业重金招聘Python工程师标准>>> 访问:https://github.com/yangbajing/scala-applications/tree/master ...
- 手把手教你学javaweb(五)文件的上传和下载
javaweb项目文件的上传和下载 在进行文件的上传和下载之前,我们先把javaweb项目做一点点的改动,那就是将LoginServlet的跳转由原来的forward方式改为 redirect方式 ...
- 使用阿里云OSS实现文件的上传、下载、删除及修改功能
一.配置OSS相关配置信息 1.要配置 OSS 相关配置信息,您可以按照以下步骤操作: 登录阿里云控制台,进入 OSS 控制台,创建一个新的 OSS Bucket,并记录下以下信息:Bucket 名称 ...
- java图片上传下载_java实现文件的上传和下载
1. servlet 如何实现文件的上传和下载? 1.1上传文件 参考自:http://blog.csdn.net/hzc543806053/article/details/7524491 通过前台选 ...
最新文章
- 网络带宽与传输性能的基本计算方法
- python开发安卓程序-如何使用python开发android应用
- NO.152 移动互联时代如何做企业营销
- Oracle查询和解锁表
- 近期两篇双目图像超分辨算法论文解读 |AAAI2020 SPL2020
- linux7简单应用,centos7下openTSDB简单应用
- HIT Software Construction Review Notes(0-1 Introduction to the Course)
- python自学行吗-python能够自学吗
- gvdp哪个工厂用_ppr铝塑管和ppr水管哪个更适合家装?
- OpenCV-图像处理(02、矩阵的掩膜操作)
- DDoS是什么意思?
- XPS Silverlight Reader
- R语言NBA球员数据挖掘简单实现
- 电影《乌云背后的幸福线》观后感
- HTTPS网站优化问题
- 基于 Flink + Kafka 的实时数仓建设实践(附源码)
- 异常捕获--编译开关EHsc、EHa
- 你是多卓越,才配得上深圳这座城市?
- swing界面如何增加日历功能
- 微软进军杀毒软件市场
热门文章
- 【学习教程】FVCOM流域、海洋水环境数值模拟方法及实践技术应用
- 香蕉派,修改 uboot 和 kernel 串口波特率
- 小米4c+android+6,小米4c有什么接口?小米4c有HDMI接口吗?
- 网站的 计算机主机作用是什么情况,运维堡垒主机是什么有什么作用
- 啊哈c语言一起来找茬答案,啊哈少儿编程网-啊哈C【第三章】来了! 第2节-【说几遍就几遍】 - ahalei.com...
- 樊纲王小鲁市场化指数(2000-2019年)
- 关于功耗芯片那些事(四)
- 动态相关系数dcc_【分享吧】基于DCCMVGARCH模型的期货与股票市场收益率波动的动态相关分析...
- python opencv写视频——cv2.VideoWriter()
- centos 阿帕奇无法解析php_PHP文件包含漏洞利用思路与Bypass总结手册(二)