基于策略模式和简单工厂模式实现zip、tar、rar、7z四种压缩文件格式的解压

推荐语

这篇技术文章深入探讨了基于策略模式和简单工厂模式实现四种常见压缩文件格式的解压方法。通过阅读该文章,你将了解到如何利用这两种设计模式来实现灵活、可扩展的解压功能,同时适应不同的压缩文件格式。如果你对设计模式和文件处理感兴趣或刚好碰到类似的情况,那么这篇文章绝对值得一读。它会为你打开一个新的思路,并帮助你提升在软件开发中的技能和效率。

需求描述与分析

最近我遇到了一个需求,需要上传一个压缩包文件,其中包含目录和文件。上传成功后,后台会自动解压该文件,并保持压缩包内目录和文件的结构不变,然后将该结构渲染到页面上。

这个需求其实相对来说非常简单,实现起来并不困难。为了满足需求,我们需要解决以下三个问题:

  • 压缩包文件有多种格式,例如ZIP、RAR、TAR等等,我们需要支持哪些格式?
  • 如何选择合适的技术或工具来自动化地解压这些不同格式的文件?
  • 在解压压缩包的同时,如何解析出里面的目录、文件之间的关系和结构,以便被正确地渲染到前端页面上?

实现原理

先解决第一个问题:压缩包文件的格式有很多,要支持多少种?

下面这四种相对比较常见,支持解析的格式暂时定为这四种:

  • ZIP (.zip):ZIP 是最常见的压缩文件格式之一,多数操作系统都默认支持它。ZIP 文件通常用于在网络或电子邮件中传输文件,以及在本地计算机上备份和存档文件。
  • TAR (.tar):TAR 是一种用于将多个文件和文件夹打包成一个单独的文件的文件格式。与其他压缩格式不同,TAR 不会对文件进行压缩,仅用于打包和归档文件。
  • RAR (.rar):RAR 是另一种常见的压缩文件格式,它可以压缩许多文件,并提供比 ZIP 更好的压缩率。RAR 格式通常用于将大型文件分成多个部分,以便在互联网上分发和下载。
  • 7Z (.7z):7Z 是一种开源的压缩文件格式,可提供比其他压缩文件格式更高的压缩率。7Z 文件通常用于压缩大型文件和文件夹,以便在网络上传输和存储。

第二个问题:如何解压这些不同格式的文件?

支付的压缩文件的格式暂定为上面四种格式的压缩包文件那如何对这些不同格式的压缩包文件进行解压操作呢?如文章标题,基于策略模式和 简单工厂模式先实现上面四种格式的压缩文件解压,具体怎么实现呢?

  • 定义一个解压策略的抽象接口,内部定义一个抽象方法,作用是执行解压操作,但是这里不提供具体实现,具体实现由不同格式的具体解压策略来实现,这个方法的输入参数采用java标准的输入流,方便与其他业务对象,输出参数则是一个List集合类型,集合内存储的是解压文件的FileObj对象;
  • 分别根据四种压缩文件格式,定义四个不同的解压策略实现类,这四个解压策略实现类来实现上面的解压策略抽象接口,重写接口的抽象方法,方法的业务逻辑是来执行具体格式的压缩文件的解压操作;
  • 再定义一个解压策略的简单工厂类,用于根据不同的后缀,生成不同的解压策略,这个工工厂类最大的意义莫过于实现了解压客户端业务与具体解压操作的解耦;如果后续业务变更,需要新增其他格式的压缩文件解压,可以实现一个具体的解压策略类,然后再扩展一下这里的工厂类;解压客户端业务不需要做任何变更,这也符合设计模式中的单一职责;

第三个问题:压缩包文件解压的同时,怎么解析出压缩包里面的目录、文件的关系和结构?

  • 从压缩包文件中解析出文件后,解压结果是一个包含FileObj对象的集合,FileObj对象包含了三个属性:1、文件在压缩包内的全路径(包含有文件名称、后缀);2、文件后缀;3、文件的全部字节(这里封装为字节的目的是方便,后面的处理,如写入本地、写入远程文件服务器等);
  • 从解析结果中提取出文件在压缩包内的全路径;然后再进一步处理,转换为一个包含FileDir类型对象的集合,FileDir对象中有两个重要属性当前文件或目录的唯一标识和其父级的唯一标识,据此可以构建出一个树形结构,也就还原了压缩包里面的目录、文件的关第和结构;

实现方案

mavne依赖

  <dependency><groupId>org.apache.commons</groupId><artifactId>commons-compress</artifactId><version>1.24.0</version></dependency><dependency><groupId>net.sf.sevenzipjbinding</groupId><artifactId>sevenzipjbinding</artifactId><version>16.02-2.01</version></dependency><dependency><groupId>org.tukaani</groupId><artifactId>xz</artifactId><version>1.9</version></dependency><dependency><groupId>com.github.junrar</groupId><artifactId>junrar</artifactId><version>7.5.5</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><dependency><groupId>net.sf.sevenzipjbinding</groupId><artifactId>sevenzipjbinding-all-platforms</artifactId><version>16.02-2.01</version></dependency><dependency><groupId>org.apache.ant</groupId><artifactId>ant</artifactId><version>1.10.12</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.23</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.32</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.6</version></dependency>

FileObj和FileDir

FileObj:从压缩包中解压出的每一个文件,封装到一个FileObj对象中,FileObj对象有三个属性:1、文件在压缩包内的全路径(包含有文件名称、后缀);2、文件后缀;3、文件的全部字节(这里封装为字节的目的是方便,后面的处理,如写入本地、写入远程文件服务器等)

@Data
public class FileObj {/*** 压缩包内文件全路径*/private String filePath;/*** 压缩包内文件后缀*/private String suffix;/*** 压缩包内文件的字节*/private byte[] bytes;
}

FileDir:把压缩包文件里的每一个目录封装成一个FileDir对象,FileDir对象包有六个属性:1、目录名称;2、目录级别,根目录为1,其子目录依次递增;3、父级目录的名称,如果是根目录,则父级目录为空;4、目录的唯一标识;5、父级目录的唯一标识;6、当前节点的孩子节点;这里注意一下要重写一个hashCode和equals方法,因为在解析压缩文件内的目录、文件的关系和结构时,使用了Set集合的去重功能;

@Data
public class FileDir{/*** 类型,1:目录;2:文件;*/private Integer docType=1;/*** 目录名称*/private String docName;/*** 目录级别,根目录为1,其子目录依次递增*/private Integer level;/*** 父级目录名称*/private String parentDocName;/*** 目录唯一标识*/private Long id;/*** 父级目录唯一标识*/private Long parentId;/*** 当前节点的孩子节点*/private List<FileDir> children;@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;FileDir fileDir = (FileDir) o;if (!Objects.equals(docType, fileDir.docType)) return false;if (!Objects.equals(docName, fileDir.docName)) return false;if (!Objects.equals(level, fileDir.level)) return false;if (!Objects.equals(parentDocName, fileDir.parentDocName))return false;if (!Objects.equals(id, fileDir.id)) return false;if (!Objects.equals(parentId, fileDir.parentId)) return false;return Objects.equals(children, fileDir.children);}@Overridepublic int hashCode() {int result = docType != null ? docType.hashCode() : 0;result = 31 * result + (docName != null ? docName.hashCode() : 0);result = 31 * result + (level != null ? level.hashCode() : 0);result = 31 * result + (parentDocName != null ? parentDocName.hashCode() : 0);result = 31 * result + (id != null ? id.hashCode() : 0);result = 31 * result + (parentId != null ? parentId.hashCode() : 0);result = 31 * result + (children != null ? children.hashCode() : 0);return result;}
}

UnZipStrategy

UnZipStrategy:是解压策略的抽象接口,内部定义一个抽象方法,作用是执行解压操作,具体实现由不同格式的具体解压策略来实现,这个方法的输入参数采用java标准的输入流,方便与其他业务对象,输出参数则是一个List集合类型,集合内存储的是解压文件的FileObj对象;

public interface UnZipStrategy {List<FileObj> analysis(InputStream inputStream);
}

ZipAnalysisStrategy

ZipAnalysisStrategy:是zip格式的压缩文件的解压策略,实现UnZipStrategy抽象接口,具体来执行zip格式的压缩文件的解压操作;

@Slf4j
public class ZipAnalysisStrategy implements UnZipStrategy {@Overridepublic List<FileObj> analysis(InputStream inputStream) {List<FileObj> list = new ArrayList<>();ArchiveInputStream ais = new ZipArchiveInputStream(inputStream);ArchiveEntry entry;try {while ((entry = ais.getNextEntry()) != null) {if (!entry.isDirectory()) {FileObj fileObj = extractFile(ais, entry);list.add(fileObj);}}ais.close();inputStream.close();} catch (IOException e) {log.error("zip格式压缩文件在解压时发生错误:",e);throw new RuntimeException(e);}return list;}private FileObj extractFile(ArchiveInputStream ais, ArchiveEntry entry) throws IOException {String name = entry.getName();log.info("解析文件:{}",name);int index = name.lastIndexOf(".");String suffix = name.substring(index + 1);FileObj fileObj = new FileObj();fileObj.setFilePath(entry.getName());fileObj.setSuffix(suffix);ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int length;while ((length = ais.read(buffer)) > 0) {byteArrayOutputStream.write(buffer, 0, length);}byte[] bytes = byteArrayOutputStream.toByteArray();fileObj.setBytes(bytes);byteArrayOutputStream.close();return fileObj;}
}

TarAnalysisStrategy

TarAnalysisStrategy:是tar格式的压缩文件的解压策略,实现UnZipStrategy抽象接口,具体来执行tar格式的压缩文件的解压操作;

@Slf4j
public class TarAnalysisStrategy implements UnZipStrategy {@Overridepublic List<FileObj> analysis(InputStream inputStream) {List<FileObj> list=new ArrayList<>();FileObj fileObj;TarInputStream tarInputStream = new TarInputStream(inputStream, 1024 * 2);TarEntry entry;try {while ((entry = tarInputStream.getNextEntry()) != null) {if (!entry.isDirectory()) {fileObj=new FileObj();String name = entry.getName();int index = name.lastIndexOf(".");String suffix = name.substring(index + 1);fileObj.setFilePath(name);fileObj.setSuffix(suffix);int count;byte data[] = new byte[2048];ByteArrayOutputStream outputStream = new ByteArrayOutputStream();while ((count = tarInputStream.read(data)) != -1) {outputStream.write(data, 0, count);}byte[] byteArray = outputStream.toByteArray();outputStream.close();fileObj.setBytes(byteArray);list.add(fileObj);}}inputStream.close();tarInputStream.close();} catch (IOException e) {log.error("tar格式压缩文件在解压时发生错误:",e);throw new RuntimeException(e);}return list;}
}

SevenZAnalysisStrategy

SevenZAnalysisStrategy:是7z格式的压缩文件的解压策略,实现UnZipStrategy抽象接口,具体来执行7z格式的压缩文件的解压操作;

@Slf4j
public class SevenZAnalysisStrategy implements UnZipStrategy {@Overridepublic List<FileObj> analysis(InputStream inputStream) {List<FileObj> list = new ArrayList<>();FastByteArrayOutputStream read = IoUtil.read(inputStream);byte[] byteArray = read.toByteArray();SeekableInMemoryByteChannel seekableInMemoryByteChannel = new SeekableInMemoryByteChannel(byteArray);try {SevenZFile sevenZFile = new SevenZFile(seekableInMemoryByteChannel);// 创建输出目录SevenZArchiveEntry entry;while ((entry = sevenZFile.getNextEntry()) != null) {if (!entry.isDirectory()) {FileObj fileObj = new FileObj();String name = entry.getName();log.info("解析文件:{}",name);int index = name.lastIndexOf(".");String suffix = name.substring(index + 1);fileObj.setFilePath(name);fileObj.setSuffix(suffix);ByteArrayOutputStream outputStream = new ByteArrayOutputStream();int len = 0;byte[] b = new byte[2048];while ((len = sevenZFile.read(b)) != -1) {outputStream.write(b, 0, len);}byte[] bytes = outputStream.toByteArray();outputStream.close();fileObj.setBytes(bytes);list.add(fileObj);}}inputStream.close();read.close();seekableInMemoryByteChannel.close();} catch (IOException e) {log.error("7z格式的压缩文件在解压时发生错误:",e);throw new RuntimeException(e);}return list;}
}

RarAnalysisStrategy

RarAnalysisStrategy:是rar格式的压缩文件的解压策略,实现UnZipStrategy抽象接口,具体来执行rar格式的压缩文件的解压操作;这个格式的压缩文件解压起来稍微有点费力,用到了一个回调RarExtractCallback类,这个RarExtractCallback类实现IArchiveExtractCallback接口,重写getStream()和setOperationResult(),来实现压缩文件的解压;

@Slf4j
public class RarAnalysisStrategy implements UnZipStrategy {@Overridepublic List<FileObj> analysis(InputStream inputStream) {try {byte[] bytes = IoUtil.readBytes(inputStream);ByteArrayStream byteArrayStream = new ByteArrayStream(bytes, false);IInArchive inArchive = SevenZip.openInArchive(null, byteArrayStream);int[] in = new int[inArchive.getNumberOfItems()];for (int i = 0; i < in.length; i++) {in[i] = i;}//使用回调函数RarExtractCallback rarExtractCallback = new RarExtractCallback(inArchive);inArchive.extract(in, false, rarExtractCallback);byteArrayStream.close();inputStream.close();return rarExtractCallback.getFileObjs();} catch (IOException e) {log.error("rar格式的压缩文件在解压时发生错误:", e);throw new RuntimeException(e);}}
}
@Slf4j
public class RarExtractCallback implements IArchiveExtractCallback {private Integer index;private IInArchive inArchive;private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();@Getterprivate List<FileObj> fileObjs=new ArrayList<>();public RarExtractCallback(IInArchive inArchive) {this.inArchive = inArchive;}@Overridepublic void setCompleted(long arg0) throws SevenZipException {}@Overridepublic void setTotal(long arg0) throws SevenZipException {}@Overridepublic ISequentialOutStream getStream(int index, ExtractAskMode extractAskMode) throws SevenZipException {this.index=index;boolean isFolder = (boolean) inArchive.getProperty(index, PropID.IS_FOLDER);return data -> {if (!isFolder) {try {outputStream.write(data);} catch (IOException e) {log.error("rar格式的压缩文件在解压时发生错误:",e);throw new RuntimeException(e);}}return data.length;};}@Overridepublic void prepareOperation(ExtractAskMode arg0) throws SevenZipException {}@Overridepublic void setOperationResult(ExtractOperationResult extractOperationResult) throws SevenZipException {String path = (String) inArchive.getProperty(index, PropID.PATH);boolean isFolder = (boolean) inArchive.getProperty(index, PropID.IS_FOLDER);if (!isFolder) {FileObj fileObj=new FileObj();fileObj.setFilePath(path);log.info("解析文件:{}",path);int index1 = path.lastIndexOf(".");String suffix = path.substring(index1 + 1);fileObj.setSuffix(suffix);byte[] byteArray = outputStream.toByteArray();outputStream.reset();fileObj.setBytes(byteArray);fileObjs.add(fileObj);}}
}

UnZipStrategyFactory

UnZipStrategyFactory:解压策略的简单工厂类,用于根据不同的后缀,生成不同的解压策略,最大的意义莫过于实现了解压主业务与具体解压操作的解耦;

public class UnZipStrategyFactory {public static UnZipStrategy generate(String suffix){UnZipStrategy strategy;switch (suffix){case "zip":strategy=new ZipAnalysisStrategy();break;case "tar":strategy=new TarAnalysisStrategy();break;case "rar":strategy=new RarAnalysisStrategy();break;case "7z":strategy=new SevenZAnalysisStrategy();break;default:strategy=null;break;}return strategy;}
}

FileDirProcessor

FileDirProcessor:用于从压缩包里解压出的文件列表里,解析出目录与文件的结构,即包含FileDir对象的集合;

public class FileDirProcessor {public static List<FileDir> process(List<String> list) {//解析和去重Set<FileDir> sets = new HashSet<>();if (CollUtil.isNotEmpty(list)) {for (String dirStr : list) {String[] split = dirStr.split("/");if (split.length > 0) {if (split.length == 1) {FileDir fileDir = new FileDir();fileDir.setDocName(split[0]);fileDir.setDocType(2);fileDir.setLevel(1);fileDir.setParentDocName("");sets.add(fileDir);} else {for (int i = 0; i < split.length; i++) {FileDir fileDir = new FileDir();fileDir.setDocName(split[i]);fileDir.setLevel(i + 1);fileDir.setDocType(1);if (i == 0) {fileDir.setParentDocName("");}if (i == (split.length - 1)) {fileDir.setDocType(2);fileDir.setParentDocName(split[i - 1]);}if (i != 0 && i != split.length - 1) {fileDir.setParentDocName(split[i - 1]);}sets.add(fileDir);}}}}}if (CollUtil.isNotEmpty(sets)) {//设置idMap<String, Long> map = new HashMap<>();Snowflake snowflake = IdUtil.getSnowflake();for (FileDir fileDir : sets) {long id = snowflake.nextId();fileDir.setId(id);map.put(fileDir.getLevel() + fileDir.getDocName(), id);}//设置父idfor (FileDir fileDir : sets) {if (fileDir.getLevel() == 1) {fileDir.setParentId(0L);} else {Long parentId = map.get((fileDir.getLevel() - 1) + fileDir.getParentDocName());fileDir.setParentId(parentId);}}}return new ArrayList<>(sets);}
}

FileDirTree

FileDirTree:在FileDirProcessor类的解析结果中定义了两个关键东西,当前文件或目录的唯一标识id和父id,FileDirTree的作用用在于把FileDirProcessor类的解析结果转换成一种树形结构,以便在页面上渲染展示;

public class FileDirTree {public List<FileDir> nodeList;public FileDirTree(List<FileDir> nodeList) {this.nodeList = nodeList;}private List<FileDir> getRootNode() {List<FileDir> rootNodeList = new ArrayList<>();for (FileDir treeNode : nodeList) {if (0 == treeNode.getParentId()) {rootNodeList.add(treeNode);}}return rootNodeList;}public List<FileDir> buildTree() {List<FileDir> treeNodes = new ArrayList<FileDir>();for (FileDir treeRootNode : getRootNode()) {treeRootNode = buildChildTree(treeRootNode);treeNodes.add(treeRootNode);}return treeNodes;}private FileDir buildChildTree(FileDir pNode) {List<FileDir> childTree = new ArrayList<FileDir>();for (FileDir treeNode : nodeList) {if (treeNode.getParentId().equals(pNode.getId())) {childTree.add(buildChildTree(treeNode));}}pNode.setChildren(childTree);return pNode;}}

单元测试

    @Testpublic void test4() throws IOException {String fileName = "e:/zip/test.zip";int index = fileName.lastIndexOf(".");String suffix = fileName.substring(index + 1);//构建解压策略UnZipStrategy unZipStrategy = UnZipStrategyFactory.generate(suffix);//开始解压List<FileObj> analysis = unZipStrategy.analysis(Files.newInputStream(Paths.get(fileName)));//从解压结果中获取解压文件的全路径List<String> list = analysis.stream().map(FileObj::getFilePath).collect(Collectors.toList());
//        log.info(JSONUtil.toJsonStr(list));
//        this.saveFile(analysis, suffix);//根据解压文件的全路径解析出压缩包内文件和目录的树形结构List<FileDir> process = FileDirProcessor.process(list);FileDirTree fileDirTree = new FileDirTree(process);List<FileDir> tree = fileDirTree.buildTree();log.info(JSONUtil.toJsonStr(tree));}

源码下载

如果想获取完整的示例代码,可以从下面这个地址clone:

凡夫贩夫 / unzip-demo · GitCode

写在最后

感谢您阅读我的文章,如果您觉得我的内容对您有所启发或帮助,欢迎关注我并给我点赞。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/584201.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Springboot配置http-Only

项目框架 jdk1.8、springboot2.5.10 情况一 项目中未使用&#xff08;权限认证框架&#xff1a;Sa-Token&#xff09; application.yml文件内增加配置 server.servlet.session.cookie.http-onlytrueserver.servlet.session.cookie.securetrue (此条配置建议也加上) 情况二…

课题学习(十八)----捷联测试电路设计与代码实现(基于MPU6050和QMC5883L)

一、 电路设计 本周主要工作是在项目上&#xff0c;抽空做了一个跟本课题相关的电路板&#xff0c;之前用开发板来做测试&#xff0c;MPU6050和QMC5883L都是用杜邦线连接的&#xff0c;导致接线很乱&#xff0c;也不美观&#xff0c;当然也不符合“捷联”的定义。   下面是电…

【领域驱动设计】模式--通用语言(Ubiquitous language)

一.前言 有道无术术可求&#xff0c;有术无道止于术。方法论的形成都是为了让我们能够更高效&#xff0c;系统的解决问题&#xff0c;而不至于遇到问题不知所措。 二.通用语言的必要性 相信大家在实际的软件开发流程过程中&#xff0c;经常会遇到参照 Prd原型 编码出的系统与实…

SSM驾校预约管理系统----计算机毕业设计

项目介绍 本项目分为管理员、教练、学员三种角色&#xff0c; 管理员角色包含以下功能&#xff1a; 学员管理、教练管理、车辆管理、关系管理、车辆维修管理、个人中心等功能。 教练角色包含以下功能&#xff1a; 我的课程、我的学员、车辆中心、个人中心等功能。 学员角色包…

zabbix添加监控主机(agent)并告警

一、添加监控主机 总体来说&#xff0c;在被监控主机上安装部署zabbix-agent&#xff0c;并修改配置文件&#xff08;zabbix_agentd.conf&#xff09;的参数。然后在zabbix 服务端zabbix-get检查是否可以监控。如果可以了&#xff0c;就可以在web页面添加了&#xff0c;要监控…

Ubuntu16.04 安装Anaconda

步骤 1&#xff1a; 去官网下载安装包,链接如下: https://repo.anaconda.com/archive/ 找到对应版本下载至本地电脑&#xff0c;并上传至服务器。 步骤2: 通过命令解压 sh Anaconda3-2023.03-0-Linux-x86_64.sh 一路选择yes或则回车&#xff0c;直到安装成功出现下面画面&…

Spring高手之路-@Autowired和@Resource注解异同点

目录 相同点 不同点 1.来源不同。 2.包含的属性不同 3.匹配方式&#xff08;装配顺序&#xff09;不同。 ​编辑 4.支持的注入对象类型不同 5.应用地方不同 相同点 都可以实现依赖注入&#xff0c;通过注解将需要的Bean自动注入到目标类中。都可以用于注入任意类型的Bean…

vue如何实现局部刷新?

应用场景&#xff1a; 比如你要切换tap栏实现刷新下面form表单等&#xff0c;相当于刷新页面。 如何使用如下&#xff1a; <div v-if"isReloadData"> 比如你想刷新那个位置就把 v-if"isReloadData"写到那个标签上 </div> 在data中定义刷新标…

如何本地搭建FastDFS文件服务器并实现远程访问【内网穿透】

文章目录 前言1. 本地搭建FastDFS文件系统1.1 环境安装1.2 安装libfastcommon1.3 安装FastDFS1.4 配置Tracker1.5 配置Storage1.6 测试上传下载1.7 与Nginx整合1.8 安装Nginx1.9 配置Nginx 2. 局域网测试访问FastDFS3. 安装cpolar内网穿透4. 配置公网访问地址5. 固定公网地址5.…

基于零和收益的DEA模型研究python实现

传统的DEA模型往往假设相关的是相互独立的,此时DEA模型只能计算出相对效率,无法进行效率的调整。如在碳排放分配问题上,碳排放总量保持不变,利用DEA模型只能计算出每个省份分配的相对效率,这在讨论固定资源下的碳排放配额分配问题时具有明显的局限性。在这种情况下,利用Z…

软件测试/测试开发丨Windows Appium环境搭建

windows 版本 Appium 环境搭建 安装 nodejs 下载.msi文件 https://nodejs.org/en/download/ 注意&#xff1a; 1、下载12.*版本双击安装即可。 2、无须配置环境变量,直接重启一个 cmd 输入下面的命令&#xff0c;能够查看这两个版本号即安装成功。 安装 appium desktop 直…

js对象方法大全(开发必会)

目录 前言 assgin(对象合并) 参数 功能 返回值 测试 结果 结论 create(以源对象为原型创建新对象) 参数 功能 返回值 测试 结果 结论 defineProperties(对属性进行具体定义) 参数 功能 返回值 测试 结果 结论 defineProperty(重写或定义新属性) 参数 功…

无论男孩女孩都要尽情打扮

这款柔软又细腻的开衫外套 上身体验感很不错的哈 舒适软糯百搭还透气&#xff0c;抗起球的面料 黑灰两色简单大方 胸前加上了流行的刺绣设计&#xff0c;可爱又精致 单穿内搭都可&#xff0c;现在天气还比较冷 外面可以套个羽绒服之类的 时尚叠穿风&#xff0c;韩系范儿…

【揭秘】技术同学申请专利的惊人好处,你绝对不能错过!

今天跟大家分享一下&#xff0c;从一名技术工程师&#xff08;程序员&#xff09;的角度&#xff0c;为什么要写专利以及如何去申请专利&#xff1f; 专利的本质 首先就是要科普一下&#xff0c;并不是说一定要做出来的某个东西&#xff0c;才能够申请专利&#xff0c;和想象…

Java线程池ThreadPoolExecutor源码解析

Java线程池ThreadPoolExecutor源码解析 1.ThreadPoolExecutor的构造实现 以jdk8为准&#xff0c;常说线程池有七大参数&#xff0c;通常而言&#xff0c;有四个参数是比较重要的 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit …

虚拟机迁移技术原理与应用

虚拟机迁移技术主要应用于两种场景&#xff1a; 第一种&#xff0c;随着现在虚拟化的发展&#xff0c;传统it架构的物理机需迁移到虚拟机上&#xff0c;实现负载均衡、资源优化等目的。 第二种&#xff0c;将虚拟机从一个虚拟化平台迁移到另一个虚拟化平台&#xff0c;可以是…

MySQL的高级SQL语句

目录 1.mysql高阶查询 select&#xff1a;显示表格中一个或数个字段的所有数据记录 distinct&#xff1a;不显示重复的数据记录 where&#xff1a;有条件查询 AND OR &#xff1a;且 或 in&#xff1a;显示已知值的数据记录 between&#xff1a;显示两个值范围内的数据记…

nrm的保姆级使用教程

&#x1f4e2; 鸿蒙专栏&#xff1a;想学鸿蒙的&#xff0c;冲 &#x1f4e2; C语言专栏&#xff1a;想学C语言的&#xff0c;冲 &#x1f4e2; VUE专栏&#xff1a;想学VUE的&#xff0c;冲这里 &#x1f4e2; CSS专栏&#xff1a;想学CSS的&#xff0c;冲这里 &#x1f4…

【UE 游戏模板】 游戏分类(RPG、RST等)

目录 0 引言1 游戏分类1.1 角色扮演游戏&#xff08;RPG&#xff09;1.2 第一人称射击游戏&#xff08;FPS&#xff09;1.3 即时策略游戏&#xff08;RTS&#xff09;1.4 VR游戏1.5 集换式卡牌游戏&#xff08;TCG&#xff09;1.5 塔防游戏&#xff08;Tower Defense Games&…

【Web】Ctfshow Thinkphp5 非强制路由RCE漏洞

目录 非强制路由RCE漏洞 web579 web604 web605 web606 web607-610 前面审了一些tp3的sql注入,终于到tp5了&#xff0c;要说tp5那最经典的还得是rce 下面介绍非强制路由RCE漏洞 非强制路由RCE漏洞原理 非强制路由相当于开了一个大口子&#xff0c;可以任意调用当前框…