java多线程文件下载器

文章目录

  • 1.简介
  • 2.文件下载的核心
  • 3.文件下载器的基础代码
    • 3.1 HttpURLConnection
    • 3.2 用户标识
  • 4.下载信息
    • 4.1 计划任务
    • 4.2 ScheduledExecutorService
      • 🍀 schedule方法
      • 🍀 scheduleAtFixedRate方法
      • 🍀 scheduleWithFixedDelay方法
  • 5.线程池简介
    • 5.1 ThreadPoolExecutor 构造方法参数
    • 5.2 线程池工作过程
    • 5.3 线程池的状态
    • 5.4 线程池的关闭
    • 5.5 工作队列
  • 6.代码实现
    • 6.1 环境搭建
      • 🍀 基本信息
      • 🍀 创建项目
    • 6.2 实现逻辑
    • 6.3 项目结构
    • 6.4 类代码
      • 🍀 constant 包
        • 📌 Constant
      • 🍀 util 包
        • 📌 FileUtils
        • 📌 HttpUtils
        • 📌 LogUtils
      • 🍀 core 包
        • 📌 DownloadInfoThread
        • 📌 DownloaderTask
        • 📌 Downloader
      • 🍀 Main 主类

1.简介

该项目应用的知识点包括:

  • RandomAccessFile 类的运用
  • HttpURLConnection 类的运用
  • 线程池的使用
  • 原子类 LongAdder 的运用
  • CountDownLatch 类的运用
  • ScheduledExecutorService 类的运用

2.文件下载的核心

从互联网下载文件有点类似于我们将本地某个文件复制到另一个目录下,也会利用 IO 流进行操作。对于从互联网下载,还需要将本地和下载文件所在的服务器建立连接。

image-20231107124520655

3.文件下载器的基础代码

3.1 HttpURLConnection

从互联网中下载文件的话,需要与文件所在的服务器建立连接,这里可以使用 jdk 提供的 java.net.HttpURLConnection 类来帮助我们完成这个操作。jdk11中有提供 java.net.http.HttpClient 类来替代 HttpURLConnection,由于现在使用的是 jdk8,因此先不用 jdk11 中的 HttpClient。除此之外还有一些其他第三方提供类可以执行类似的操作,这里就不赘述了。

3.2 用户标识

我们通过浏览器访问某个网站的时候,会将当前浏览器的版本,操作系统版本等信息的标识发送到网站所在的服务器中。当用程序代码去访问网站时,需要将这个标识发送过去。

Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1

4.下载信息

4.1 计划任务

文件下载的时候最好能够展示出下载的速度,已下载文件大小等信息。这里可以每隔一段时间来获取文件的下载信息,比如间隔 1 秒获取一次,然后将信息打印到控制台。文件下载是一个独立的线程,另外还需要再开启一个线程来间隔获取文件的信息。java.util.concurrent.ScheduledExecutorService 这个类可以帮助我们来实现此功能。

4.2 ScheduledExecutorService

在该类中提供了一些方法可以帮助开发者实现间隔执行的效果,下面列出一些常见的方法及其参数说明。我们可以通过下面方式来获取该类的对象,其中 1 标识核心线程的数量。

ScheduledExecutorService s = Executors.newScheduledThreadPool(1);

🍀 schedule方法

该方法是重载的,这两个重载的方法都是有 3 个形参,只是第一个形参不同。

参数含义
Runnable / Callable<V>可以传入这两个类型的任务
long delay延时的时间数量
TimeUnit unit时间单位

该方法的作用是让任务按照指定的时间延时执行。

🍀 scheduleAtFixedRate方法

该方法的作用是按照指定的时间延时执行,并且每隔一段时间再继续执行。

参数含义
Runnable command执行的任务
long initialDelay延时的时间数量
long period间隔的时间数量
TimeUnit unit时间单位

倘若在执行任务的时候,耗时超过了间隔时间,则任务执行结束之后直接再次执行,而不是再等待间隔时间执行。

🍀 scheduleWithFixedDelay方法

该方法的作用是按照指定的时间延时执行,并且每隔一段时间再继续执行。

参数含义
Runnable command执行的任务
long initialDelay延时的时间数量
long period间隔的时间数量
TimeUnit unit时间单位

在执行任务的时候,无论耗时多久,任务执行结束之后都会等待间隔时间之后再继续下次任务。

5.线程池简介

线程在创建,销毁的过程中会消耗一些资源,为了节省这些开销,jdk 添加了线程池。线程池节省了开销,提高了线程使用的效率。阿里巴巴开发文档中建议在编写多线程程序的时候使用线程池。

5.1 ThreadPoolExecutor 构造方法参数

在 juc 包下提供了 ThreadPoolExecutor 类,可以通过该类来创建线程池,这个类中有4个重载的构造方法,最核心的构造方法是有7个形参的,这些参数所代表的意义如下:

参数含义
corePoolSize线程池中核心线程的数量
maximumPoolSize线程池中最大线程的数量,是核心线程数量和非核心线程数量之和
keepAliveTime非核心线程空闲的生存时间
unitkeepAliveTime 的生存时间单位
workQueue当没有空闲的线程时,新的任务会加入到 workQueue 中排队等待
threadFactory线程工厂,用于创建线程
handler拒绝策略,当任务太多无法处理时的拒绝策略

5.2 线程池工作过程

image-20231108082712983

5.3 线程池的状态

状态说明
RUNNING创建线程池之后的状态是 RUNNING
SHUTDOWN该状态下,线程池就不会接收新任务,但会处理阻塞队列剩余任务,相对温和
STOP该状态下会中断正在执行的任务,并抛弃阻塞队列任务,相对暴力
TIDYING任务全部执行完毕,活动线程为 0 即将进入终止
TERMINATED线程池终止

5.4 线程池的关闭

线程池使用完毕之后需要进行关闭,提供了以下两种方法进行关闭。

方法说明
shutdown()该方法执行后,线程池状态变为 SHUTDOWN,不会接收新任务,但是会执行完已提交的任务,此方法不会阻塞调用线程的执行。
shutdownNow()该方法执行后,线程池状态变为 STOP,不会接收新任务,会将队列中的任务返回,并用 interrupt 的方式中断正在执行的任务。

5.5 工作队列

jdk 中提供的一些工作队列 workQueue。

队列说明
SynchronousQueue直接提交队列
ArrayBlockingQueue有界队列,可以指定容量
LinkedBlockingDeque无界队列
PriorityBlockingQueue优先任务队列,可以根据任务优先级顺序执行任务

6.代码实现

6.1 环境搭建

🍀 基本信息

  • 开发工具:IDEA
  • JDK 版本:8
  • 项目编码:utf-8

🍀 创建项目

在开发工具中创建一个 javase 项目即可,无需导入第三方 jar 依赖。

6.2 实现逻辑

  1. 先判断是否已存在重复文件,该步骤其实可忽略,因为最终下载合并的文件名已采用时间戳进行了唯一标识;
  2. 启动一个线程每隔一秒打印下载情况;
  3. 切分任务,多线程分快下载;
  4. 全部块文件下载完毕,合并分块文件;
  5. 合并分块文件完毕,清理分块文件;
  6. 释放资源,关闭线程池和连接对象。

6.3 项目结构

image-20231108091928709

包名作用
constant存放常量类的包
core存放了下载器核心类的包
util存放工具类的包
Main主类

6.4 类代码

🍀 constant 包

📌 Constant
/*** Description: 存放项目常量** @Author 狐狸半面添* @Create 2023/11/6 1:22* @Version 1.0*/
public class Constant {/*** 指定下载目录的存放位置*/public static final String PATH = "D:\\download\\";public static final double MB = 1024d * 1024d;public static final double KB = 1024d;/*** 每次读取的字节大小*/public static final int BYTE_SIZE = 1024 * 100;/*** 块文件(临时文件)的后缀*/public static final String PART_FILE_SUFFIX = ".temp";/*** 线程数量*/public static final int THREAD_NUM = 5;// 创建存放位置的代码// public static void main(String[] args) {//     File file = new File("D:\\download");//     if (!file.exists()) {//         file.mkdir();//     }// }
}

🍀 util 包

📌 FileUtils
/*** Description: 文件相关工具** @Author 狐狸半面添* @Create 2023/11/6 11:46* @Version 1.0*/
public class FileUtils {/*** 获取本地文件的大小** @param path 文件路径* @return 文件大小*/public static long getFileContentLength(String path) {File file = new File(path);return file.exists() && file.isFile() ? file.length() : 0;}
}
📌 HttpUtils
/*** Description: Http 相关工具类** @Author 狐狸半面添* @Create 2023/11/6 1:06* @Version 1.0*/
public class HttpUtils {private static long id = System.currentTimeMillis();public static void change() {id = System.currentTimeMillis();}/*** 获取下载的文件大小** @param url 下载文件链接* @return 文件大小* @throws IOException*/public static long getHttpFileContentLength(String url) throws IOException {int contentLength;HttpURLConnection httpURLConnection = null;try {httpURLConnection = getHttpURLConnection(url);contentLength = httpURLConnection.getContentLength();} finally {if (httpURLConnection != null) {httpURLConnection.disconnect();}}return contentLength;}/*** 分块下载** @param url      下载地址* @param startPos 下载文件起始位置* @param endPos   下载文件结束位置* @return 连接对象*/public static HttpURLConnection getHttpURLConnection(String url, long startPos, long endPos) throws IOException {HttpURLConnection httpURLConnection = getHttpURLConnection(url);LogUtils.info("下载的区间是:{}-{}", startPos, endPos);if (endPos != 0) {httpURLConnection.setRequestProperty("RANGE", "bytes=" + startPos + "-" + endPos);} else {httpURLConnection.setRequestProperty("RANGE", "bytes=" + startPos + "-");}return httpURLConnection;}/*** 获取 HttpURLConnection 连接对象** @param url 文件的地址* @return HttpURLConnection 连接对象*/public static HttpURLConnection getHttpURLConnection(String url) throws IOException {URL httpUrl = new URL(url);HttpURLConnection httpURLConnection = (HttpURLConnection) httpUrl.openConnection();// 向文件所在的服务器发送标识信息httpURLConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1");return httpURLConnection;}/*** 获取下载文件的名字** @param url 下载地址* @return 文件名*/public static String getHttpFileName(String url) {String fileName;int startIndex = url.lastIndexOf("/");int endIndex = url.lastIndexOf("?");if (endIndex == -1) {fileName = url.substring(startIndex + 1);} else {fileName = url.substring(startIndex + 1, endIndex);}int pointIndex = fileName.lastIndexOf(".");return fileName.substring(0, fileName.lastIndexOf(".")) + "-" + id + fileName.substring(pointIndex);}}
📌 LogUtils
/*** Description: 日志工具类** @Author 狐狸半面添* @Create 2023/11/6 1:41* @Version 1.0*/
public class LogUtils {private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("hh:mm:ss");public static void info(String msg, Object... args) {print(msg, "-info-", args);}public static void error(String msg, Object... args) {print(msg, "-error-", args);}private static void print(String msg, String level, Object... args) {if (args != null && args.length > 0) {msg = String.format(msg.replace("{}", "%s"), args);}String threadName = Thread.currentThread().getName();System.out.println(LocalTime.now().format(FORMATTER) + "  " + threadName + level + msg);}
}

🍀 core 包

📌 DownloadInfoThread
/*** Description: 展示下载信息** @Author 狐狸半面添* @Create 2023/11/6 2:07* @Version 1.0*/
@SuppressWarnings("AlibabaUndefineMagicConstant")
public class DownloadInfoThread implements Runnable {/*** 下载文件总大小*/private final long httpFileContentLength;/*** 本次累计下载的大小*/public static volatile LongAdder downSize = new LongAdder();/*** 前一次下载的大小*/public double prevSize;public DownloadInfoThread(long httpFileContentLength) {this.httpFileContentLength = httpFileContentLength;}@Overridepublic void run() {// 计算文件总大小 单位是 MBString httpFileSize = String.format("%.2f", httpFileContentLength / Constant.MB);// 计算每秒下载速度 kbint speed = (int) ((downSize.doubleValue() - prevSize) / Constant.KB);prevSize = downSize.doubleValue();// 剩余文件的大小double remainSize = httpFileContentLength - downSize.doubleValue();// 计算剩余时间String remainTime = String.format("%.1f", remainSize / Constant.KB / speed);if ("Infinity".equalsIgnoreCase(remainTime)) {remainTime = "-";}// 已下载大小String currentFileSize = String.format("%.1f", downSize.doubleValue() / Constant.MB);String speedInfo = String.format("已下载 %smb/%smb,速度 %skb/s,剩余时间 %ss", currentFileSize, httpFileSize, speed, remainTime);System.out.print("\r");System.out.print(speedInfo);}
}
📌 DownloaderTask
/*** Description: 分块下载任务** @Author 狐狸半面添* @Create 2023/11/7 0:58* @Version 1.0*/
public class DownloaderTask implements Callable<Boolean> {private final String url;/*** 下载起始位置*/private final long startPos;/*** 下载结束位置*/private final long endPos;/*** 标识当前是哪一部分*/private final int part;private final CountDownLatch countDownLatch;public DownloaderTask(String url, long startPos, long endPos, int part, CountDownLatch countDownLatch) {this.url = url;this.startPos = startPos;this.endPos = endPos;this.part = part;this.countDownLatch = countDownLatch;}@Overridepublic Boolean call() throws Exception {// 获取文件名String httpFileName = HttpUtils.getHttpFileName(url);// 分块的文件名httpFileName = httpFileName + Constant.PART_FILE_SUFFIX + part;// 下载路径httpFileName = Constant.PATH + httpFileName;// 获取分块下载的连接HttpURLConnection httpURLConnection = HttpUtils.getHttpURLConnection(url, startPos, endPos);try (InputStream input = httpURLConnection.getInputStream();BufferedInputStream bis = new BufferedInputStream(input);RandomAccessFile accessFile = new RandomAccessFile(httpFileName, "rw");) {byte[] buffer = new byte[Constant.BYTE_SIZE];int len;// 循环读取数据while ((len = bis.read(buffer)) != -1) {// 1s 内下载的数据,通过原子类下载DownloadInfoThread.downSize.add(len);accessFile.write(buffer, 0, len);}} catch (FileNotFoundException e) {LogUtils.error("下载文件不存在 {}", url);return false;} catch (Exception e) {LogUtils.error("下载出现异常");return false;} finally {httpURLConnection.disconnect();countDownLatch.countDown();}return true;}}
📌 Downloader
/*** Description: 下载器** @Author 狐狸半面添* @Create 2023/11/6 1:21* @Version 1.0*/
public class Downloader {private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);public ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(Constant.THREAD_NUM,Constant.THREAD_NUM,0,TimeUnit.SECONDS,new ArrayBlockingQueue<>(5));private CountDownLatch countDownLatch = new CountDownLatch(Constant.THREAD_NUM);public void download(String url) {// 获取文件名String httpFileName = HttpUtils.getHttpFileName(url);// 文件下载路径httpFileName = Constant.PATH + httpFileName;// 获取本地文件的大小long localFileLength = FileUtils.getFileContentLength(httpFileName);HttpURLConnection httpURLConnection = null;DownloadInfoThread downloadInfoThread;try {// 获取连接对象httpURLConnection = HttpUtils.getHttpURLConnection(url);// 获取下载文件的总大小int contentLength = httpURLConnection.getContentLength();// 判断文件是否已下载过if (localFileLength >= contentLength) {LogUtils.info("{} 已下载完毕,无需重新下载", httpFileName);// 关闭连接对象httpURLConnection.disconnect();// 关闭线程池scheduledExecutorService.shutdownNow();poolExecutor.shutdown();return;}// 创建获取下载信息的任务对象downloadInfoThread = new DownloadInfoThread(contentLength);// 将任务交给线程执行,每隔 1s 打印一次scheduledExecutorService.scheduleAtFixedRate(downloadInfoThread, 1, 1, TimeUnit.SECONDS);// 切分任务ArrayList<Future> list = new ArrayList<>();split(url, list);countDownLatch.await();System.out.print("\r");System.out.println("分块文件下载完成");// 合并文件if (merge(httpFileName)) {// 清除临时文件clearTemp(httpFileName);}} catch (IOException | InterruptedException e) {e.printStackTrace();} finally {System.out.println("本次执行完成");// 关闭连接对象if (httpURLConnection != null) {httpURLConnection.disconnect();}// 关闭线程池scheduledExecutorService.shutdownNow();poolExecutor.shutdown();}}/*** 文件切分** @param url        文件链接* @param futureList 任务集合*/public void split(String url, ArrayList<Future> futureList) {try {// 获取下载文件大小long contentLength = HttpUtils.getHttpFileContentLength(url);// 计算切分后的文件大小long size = contentLength / Constant.THREAD_NUM;// 计算分块个数for (int i = 0; i < Constant.THREAD_NUM; i++) {// 计算下载起始位置long startPos = i * size;// 计算结束位置long endPos;if (i == Constant.THREAD_NUM - 1) {// 下载最后一块endPos = 0;} else {endPos = startPos + size - 1;}// 创建任务对象DownloaderTask downloaderTask = new DownloaderTask(url, startPos, endPos, i, countDownLatch);// 将任务提交到线程池Future<Boolean> future = poolExecutor.submit(downloaderTask);futureList.add(future);}} catch (IOException e) {throw new RuntimeException(e);}}/*** 文件合并** @param fileName 文件名* @return 是否合并成功*/public boolean merge(String fileName) {LogUtils.info("开始合并文件 {}", fileName);byte[] buffer = new byte[Constant.BYTE_SIZE];int len;try (RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw")) {for (int i = 0; i < Constant.THREAD_NUM; i++) {try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName + Constant.PART_FILE_SUFFIX + i))) {while ((len = bis.read(buffer)) != -1) {accessFile.write(buffer, 0, len);}}}LogUtils.info("文件合并完毕 {}", fileName);} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 清除临时文件** @param fileName 文件名*/public void clearTemp(String fileName) {LogUtils.info("清理分块文件");for (int i = 0; i < Constant.THREAD_NUM; i++) {String name = fileName + Constant.PART_FILE_SUFFIX + i;File file = new File(name);file.delete();}LogUtils.info("分块清除完毕");}
}

🍀 Main 主类

public class Main {public static void main(String[] args) {// 下载地址String url = null;if (args == null || args.length == 0) {while (url == null || url.trim().isEmpty()) {System.out.print("请输入下载链接:");Scanner scanner = new Scanner(System.in);url = scanner.next();}} else {url = args[0];}Downloader downloader = new Downloader();downloader.download(url);}
}

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

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

相关文章

python+pytorch人脸表情识别

概述 基于深度学习的人脸表情识别&#xff0c;数据集采用公开数据集fer2013&#xff0c;可直接运行&#xff0c;效果良好&#xff0c;可根据需求修改训练代码&#xff0c;自己训练模型。 详细 一、概述 本项目以PyTorch为框架&#xff0c;搭建卷积神经网络模型&#xff0c;训…

Zigbee—网络层地址分配机制

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;孤雏 0:21━━━━━━️&#x1f49f;──────── 4:14 &#x1f504; ◀️ ⏸ ▶️ ☰ &#x1f497;关注…

AI 时代的企业级安全合规策略

目录 漏洞分类管理的流程 安全策略管理 在扫描结果策略中定义细粒度的规则 有效考虑整个组织中的关键漏洞 确保职责分离 尝试组合拳 本文来源&#xff1a;about.gitlab.com 作者&#xff1a;Grant Hickman 在应用程序敏捷研发、敏捷交付的今天&#xff0c;让安全人员跟上…

EasyExcel 导出冻结指定行

导出的实体类 package org.jeecg.modules.eis.test;import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.write.style.*; import lombok.Getter; import lombok.Setter; import org.apache.poi.ss.usermodel.HorizontalAlignment;import…

pytorch复现_UNet

什么是UNet U-Net由收缩路径和扩张路径组成。收缩路径是一系列卷积层和汇集层&#xff0c;其中要素地图的分辨率逐渐降低。扩展路径是一系列上采样层和卷积层&#xff0c;其中特征地图的分辨率逐渐增加。 在扩展路径中的每一步&#xff0c;来自收缩路径的对应特征地图与当前特征…

css设置浏览器表单自动填充时的背景

浏览器自动填充表单内容&#xff0c;会自动设置背景色。对于一般的用户&#xff0c;也许不会觉得有什么&#xff0c;但对于要求比较严格的用户&#xff0c;就会“指手画脚”。这里&#xff0c;我们通过css属性来设置浏览器填充背景的过渡时间&#xff0c;使用户看不到过渡后的背…

win10下.net framework 3.5 | net framework 4 无法安装解决方案

.net缺失解决方案 win10 .net framework 3.5组策略设置方案一方案二 win10 .net framework 4 参考文章 win10 .net framework 3.5 组策略设置 方案一 搜索组策略&#xff0c;依次展开“计算机配置”、“管理模板”&#xff0c;然后选择“系统”&#xff0c;找到指定可选组件…

Leetcode2246. 相邻字符不同的最长路径

Every day a Leetcode 题目来源&#xff1a;2246. 相邻字符不同的最长路径 解法1&#xff1a;树形 DP 如果没有相邻节点的限制&#xff0c;那么本题求的就是树的直径上的点的个数&#xff0c;见于Leetcode543. 二叉树的直径。 考虑用树形 DP 求直径。 枚举子树 x 的所有子…

如何让群晖Audio Station公开共享的本地音频公网可访问?

文章目录 1. 本教程使用环境&#xff1a;2. 制作音频分享链接3. 制作永久固定音频分享链接&#xff1a; 之前文章我详细介绍了如何在公网环境下使用pc和移动端访问群晖Audio Station&#xff1a; 公网访问群晖audiostation听歌 - cpolar 极点云 群晖套件不仅能读写本地文件&a…

Go基础知识全面总结

文章目录 go基本数据类型bool类型数值型字符字符串 数据类型的转换运算符和表达式1. 算数运算符2.关系运算符3. 逻辑运算符4. 位运算符5. 赋值运算符6. 其他运算符运算符优先级转义符 go基本数据类型 bool类型 布尔型的值只可以是常量 true 或者 false。⼀个简单的例⼦&#…

竞赛选题 深度学习猫狗分类 - python opencv cnn

文章目录 0 前言1 课题背景2 使用CNN进行猫狗分类3 数据集处理4 神经网络的编写5 Tensorflow计算图的构建6 模型的训练和测试7 预测效果8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习猫狗分类 ** 该项目较为新颖&a…

Python基础教程之十六:Python multidict示例–将单个键映射到字典中的多个值

1.什么是multidict词典> 在python中&#xff0c;“ multidict ”一词用于指代字典&#xff0c;在字典中可以将单个键映射到多个值。例如 多重结构 multidictWithList {key1 : [1, 2, 3],key2 : [4, 5]}multidictWithSet {key1 : {1, 2, 3},key2 : {4, 5}}1. list如果要…

内核移植笔记 Cortex-M移植

常用寄存器 PRIMASK寄存器 为1位宽的中断屏蔽寄存器。在置位时&#xff0c;它会阻止不可屏蔽中断&#xff08;NMI&#xff09;和HardFault异常之外的所有异常&#xff08;包括中断&#xff09;。 实际上&#xff0c;它是将当前异常优先级提升为0&#xff0c;这也是可编程异常/…

uniapp使用vue3和ts开发小程序自定义tab栏,实现自定义凸出tabbar效果

要实现自定义的tabbar效果&#xff0c;可以使用自定义tab覆盖主tab来实现&#xff0c;当程序启动或者从后台显示在前台时隐藏自带的tab来实现。自定义一个tab组件&#xff0c;然后在里面实现自定义的逻辑。 组件中所使用的组件api可以看&#xff1a;Tabbar 底部导航栏 | uView…

Centos7下搭建H3C log服务器

rsyslogH3C 安装rsyslog服务器 关闭防火墙 systemctl stop firewalld && systemctl disable firewalld关闭selinux sed -i s/enforcing/disabled/ /etc/selinux/config && setenforce 0centos7服务器&#xff0c;通过yum安装rsyslog yum -y install rsysl…

【uniapp】六格验证码输入框实现

效果图 代码实现 <view><view class"tips">已发送验证码至<text class"tips-phone">{{ phoneNumber }}</text></view><view class"code-input-wrap"><input class"code-input" v-model"…

AI:75-基于生成对抗网络的虚拟现实场景增强

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

[量化投资-学习笔记008]Python+TDengine从零开始搭建量化分析平台-CCI和ATR

目录 1. 指标简介CCIATR 2. 程序编写题外话 1. 指标简介 将这两个指标放在一起&#xff0c;一方面是因为这两个指标都属于摆动指数&#xff0c;可以反应市场的活跃度。 另一方面是因为CCI和ATR与之前提到的EMA,MACD,布林带的三个指标的计算基础不同。之前的三个指标都是以收盘…

坐标系转换(仅作记载)

一.极坐标转换为普通坐标系 参考&#xff1a;极坐标方程与直角坐标方程的互化 - 知乎 (zhihu.com) 公式&#xff1a;&#xff08;无需考虑象限引起的正负问题&#xff09; 普通坐标系转换为极坐标系 参考&#xff1a; 极坐标怎么与直角坐标系相互转化&#xff1f; - 知乎 (zh…