java周期性线程池newScheduledThreadPool介绍,多线程下载url文件(断点下载、进度展示、网速展示、剩余时间展示)

news2024/11/19 5:45:41

文章目录

  • 一:newScheduledThreadPool(周期性线程池)
    • 1.1 特点
    • 1.2 核心线程数
    • 1.3 创建实例
    • 1.4 常用方法
      • 1.4.1 schedule方法
      • 1.4.2 scheduleAtFixedRate方法
      • 1.4.3 scheduleWithFixedDelay方法
  • 二:多线程下载展示文件总大小、剩余时间、下载速率
    • 2.1 文件总大小
    • 2.2 累计下载大小
      • 2.2.1 Volatile
      • 2.2.2 LongAdder
    • 2.3 前一次下载的大小
    • 2.4 重写run方法
  • 三:编写下载器
    • 3.1 编写http工具类
    • 3.2 核心下载功能
    • 3.3 编写分片下载的线程DownloaderTask
    • 3.4 合并文件
    • 3.5 清理临时文件
    • 3.6 编写main方法

一:newScheduledThreadPool(周期性线程池)

1.1 特点

延时启动 、定时启动 、可以自定义最大线程池数量

1.2 核心线程数

int cpuNubmer = Runtime.getRuntime().availableProcessors();

1.3 创建实例

public ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(cpuNubmer);

1.4 常用方法

1.4.1 schedule方法

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
 
/**
 * @description 测试周期性线程池ScheduledThread-schedule方法
 */
public class ScheduledThreadTest {
 
    public static void main(String[] args) throws Exception {
        ScheduledExecutorService executorService = ScheduledThread.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        // 延迟2秒执行下一个任务
        System.out.println("当前时间=" + sdf.format(new Date()));
        for (int i = 0; i < 9; i++) {
            int finalI = i;
            ScheduledFuture future = executorService.schedule(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    int value = finalI;
                    System.out.println("时间=" + sdf.format(new Date()) + ",线程=" + Thread.currentThread().getName() + ",任务=" + value);
//                    Thread.sleep(3);
                    return "call";
                }
            }, 2, TimeUnit.SECONDS);
            System.out.println(future.get());
 
        }
        executorService.shutdown();
 
    }
}

1.4.2 scheduleAtFixedRate方法

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);
  • scheduleAtFixedRate :是以上一个任务开始的时间计时,period时间过去后,检测上一个任务是否执行完毕,如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行。
  • initialDelay:表示首次延迟2秒执行
  • period :表示周期执行的时间为6秒,即表示会重复执行,重复执行的间隔时间为6秒
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
 
/**
 * 测试周期性线程池ScheduledThread-scheduleAtFixedRate方法
 * initialDelay  表示首次延迟2秒执行
 * period        表示周期执行的时间为6秒,即表示会重复执行,重复执行的间隔时间为6秒
 * scheduleAtFixedRate ,是以上一个任务开始的时间计时,period时间过去后,检测上一个任务是否执行完毕,
 * 如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行。
 */
public class ScheduledThreadTest2 {
    public static void main(String[] args) throws Exception {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        System.out.println("当前时间="+sdf.format(new Date()));
        // 延迟2秒执行下一个任务
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("begin=" + sdf.format(new Date()));
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("end=" + sdf.format(new Date()));
            }
        }, 2, 4, TimeUnit.SECONDS);
    }
//    周期性执行
//    当执行时间小于period时,下次执行时间= 当前执行开始时间+period
//    当前时间=2021-09-16 05:21:59
//    begin=2021-09-16 05:22:01
//    end=2021-09-16 05:22:05
//    begin=2021-09-16 05:22:07
//    end=2021-09-16 05:22:11
//    当执行时间大于period时,下次执行时间 = 当前执行结束时间
//    当前时间=2021-09-16 05:23:13
//    begin=2021-09-16 05:23:15
//    end=2021-09-16 05:23:20
//    begin=2021-09-16 05:23:20
//    end=2021-09-16 05:23:25
}

1.4.3 scheduleWithFixedDelay方法

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
 
/**
 * @description 测试周期性线程池ScheduledThread-scheduleWithFixedDelay方法
 * scheduleWithFixedDelay,是从上一个任务结束时开始计时,period时间过去后,再次执行下一次任务。
 */
public class ScheduleWithFixedDelayTest {
    public static void main(String[] args) throws Exception {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        System.out.println("当前时间="+sdf.format(new Date()));
        // 延迟2秒执行下一个任务
        executorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("begin=" + sdf.format(new Date()));
                try {
                    Thread.sleep(5000);
 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("end=" + sdf.format(new Date()));
            }
        }, 2, 2, TimeUnit.SECONDS);
    }
//    周期性执行查询
//    执行结果如下:当前时间延迟2秒时间后开始执行线程,下次执行的时间= 当前执行结束时间+period
//    当前时间=2021-09-16 05:13:28
//    begin=2021-09-16 05:13:31
//    end=2021-09-16 05:13:36
//    begin=2021-09-16 05:13:38
//    end=2021-09-16 05:13:43
//    begin=2021-09-16 05:13:45
//    end=2021-09-16 05:13:50
}

二:多线程下载展示文件总大小、剩余时间、下载速率

2.1 文件总大小

	/**
     * 文件总大小
     */
    public long httpFileContentSize;

2.2 累计下载大小

public static volatile LongAdder downSize = new LongAdder();

2.2.1 Volatile

Volatile关键字的作用主要有如下两个:
1.线程的可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。
2. 顺序一致性:禁止指令重排序。

2.2.2 LongAdder

LongAdder可以是计数器的增强版,高并发下性能会更好,适合频繁的更新,但是不太频繁读取,汇总统计信息时使用分成多个操作单元,不同线程更新不同的单元,只有需要汇总的时候才计算所有单元的操作。其实就是将内存中操作的变量拆分出来,让它变成多个变量(这里和ConcurrentHashMap的原理就很相似了),然后让线程去竞争这些变量,将这些变量处理完后,然后在进行求和,这样降低了变量的并发度,减少了CAS失败次数。

2.3 前一次下载的大小

	/**
     * 前一次下载的大小
     * currentDownSize - preDownSize就是一秒内下载了多少
     */
    public double preDownSize;

2.4 重写run方法

package com.sysg.file.core;

import com.sysg.file.constant.FileConstant;

import java.util.concurrent.atomic.LongAdder;

/**
 * 展示下载信息的线程
 */
public class DownloadInfoThread implements Runnable {


    /**
     * 文件总大小
     */
    public long httpFileContentSize;

    /**
     * 累计下载大小
     * Volatile关键字的作用主要有如下两个:
     * 1.线程的可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。
     * 2. 顺序一致性:禁止指令重排序。
     *
     * LongAdder可以是计数器的增强版,高并发下性能会更好,适合频繁的更新,但是不太频繁读取,
     * 汇总统计信息时使用分成多个操作单元,不同线程更新不同的单元,只有需要汇总的时候才计算所有单元的操作。
     *
     * 其实就是将内存中操作的变量拆分出来,让它变成多个变量( 这里和ConcurrentHashMap的原理就很相似了),
     * 然后让线程去竞争这些变量,将这些变量处理完后,然后在进行求和,这样降低了变量的并发度,减少了CAS失败次数
     */
    public static volatile LongAdder downSize = new LongAdder();

    /**
     * 前一次下载的大小
     * currentDownSize - preDownSize就是一秒内下载了多少
     */
    public double preDownSize;

    public DownloadInfoThread(long httpFileContentSize){
        this.httpFileContentSize = httpFileContentSize;
    }

    /**
     * 通过定时任务线程池,让其每一秒执行一次
     */
    @Override
    public void run() {
        //计算文件总大小,单位MB
        String fileSize = String.format("%.2f", httpFileContentSize/FileConstant.MB);
        //计算每秒下载速度,单位MB
        double speed = Double.parseDouble(String.format("%.2f", (downSize.doubleValue() - preDownSize)/FileConstant.MB));
        //计算后,将当前下载大小作为上一次的,currentDownSize在不断变化
        preDownSize = downSize.doubleValue();
        //剩余文件大小,文件总大小 - 本地已下载文件的大小 - 当前下载的大小
        double remainSize = httpFileContentSize - downSize.doubleValue();
        //估算剩余时间
        String remainTime = String.format("%.1f", (remainSize / FileConstant.MB / speed));
        //判断剩余时间是否为无限大
        if("Infinity".equals(remainTime)){
            remainTime = "-";
        }
        //计算已下载大小
        String alreadyDownSize = String.format("%.2f", downSize.doubleValue() / FileConstant.MB);
        String downInfo = String.format("已下载%sMB,文件总大小%sMB,下载速度%smb/s,剩余时间%ss",
                alreadyDownSize,
                fileSize,
                speed,
                remainTime);
        System.out.println("\r");
        System.out.println(downInfo);

    }
}


三:编写下载器

在这里插入图片描述

以下载QQ客户端的url为例:https://dldir1.qq.com/qqfile/qq/PCQQ9.7.9/QQ9.7.9.29059.exe

3.1 编写http工具类

package com.sysg.file.utils;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * http工具类
 */
public class HttpUtil {


    /**
     * 得到下载文件长度
     *
     * @param url url
     * @return long
     */
    public static long getHttpFileContentLength(String url){
        HttpURLConnection httpURLConnection = null;
        try {
            httpURLConnection = getHttpURLConnection(url);
            return httpURLConnection.getContentLength();
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        } finally {
            if(httpURLConnection != null){
                httpURLConnection.disconnect();
            }
        }
    }


    /**
     * 分片下载
     * @param url 下载地址
     * @param startPos 下载起始位置
     * @param endPos  下载结束位置
     * @return
     */
    public static HttpURLConnection getHttpURLConnection(String url,long startPos,long endPos){
        HttpURLConnection httpURLConnection = getHttpURLConnection(url);
        LogUtil.info("下载的区间是:{}-{}",startPos,endPos);
        if(httpURLConnection != null){
            if (endPos != 0){
                //bytes=100-200
                httpURLConnection.setRequestProperty("RANGE","BYTES="+ startPos + "-" + endPos);
            } else {
                //下载最后一部分时endPos会赋值为0,此时如果为0,就表示会下载最后一部分
                httpURLConnection.setRequestProperty("RANGE","BYTES="+ startPos + "-");
            }
            return httpURLConnection;
        }
        return null;
    }



    /**
     * 获取HttpURLConnection连接对象
     * @param url 文件的地址
     * @return
     */
    public static HttpURLConnection getHttpURLConnection(String url){
        try {
            //建立连接
            URL httpUrl = new URL(url);
            HttpURLConnection urlConnection =(HttpURLConnection)httpUrl.openConnection();
            //向文件所在服务器发送标识信息,模拟浏览器
            urlConnection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36");
            return urlConnection;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

    }

    /**
     * 获取下载文件的文件名
     * @param url
     * @return
     */
    public static String getHttpFileName(String url){
        //获取最后一次“/”出现的下标
        int index = url.lastIndexOf("/");
        String fileName = url.substring(index + 1);
        if(!fileName.endsWith(".exe")){
            fileName = fileName + ".exe";
        }
        return fileName;
    }


}

3.2 核心下载功能

/**
     * 下载器
     *
     * @param url url
     */
    public void downloader(String url){
        //获取下载的文件名
        String fileName = HttpUtil.getHttpFileName(url);
        File file = new File(FileConstant.DOWNLOAD_PATH);
        if(!file.exists()){
            file.mkdirs();
        }
        //拼接文件下载路径
        String downPath = FileConstant.DOWNLOAD_PATH + fileName;
        //获取本地文件大小
        long localFileLength = FileUtil.getFileContentLength(downPath);
        //获取连接对象
        HttpURLConnection httpURLConnection = null;
        //获取需要下载下载的文件大小
        int contentLength = 0;
        try {
            httpURLConnection = HttpUtil.getHttpURLConnection(url);
            contentLength = httpURLConnection.getContentLength();
            //判断文件是否已经下载过
            if(localFileLength >= contentLength){
                LogUtil.info("无须重新下载:{}",fileName);
                return;
            }
            //创建获取下载信息的任务对象
            DownloadInfoThread downloadInfoThread = new DownloadInfoThread(contentLength);
            /**
             * 将任务交给线程执行,每隔一秒执行一次
             * initialDelay:延迟一秒执行
             * period:每隔一秒执行一次
             */
            scheduledExecutorService.scheduleAtFixedRate(downloadInfoThread,1,1, TimeUnit.SECONDS);
            //调用切分任务的方法
            splitFile(url);
            countDownLatch.await();
            //合并文件
            if(mergeFile(fileName)){
                clearTemp(fileName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("\r");
            System.out.println("下载完成");
            if(httpURLConnection != null){
                //关掉连接对象
                httpURLConnection.disconnect();
            }
            //关闭线程
            scheduledExecutorService.shutdownNow();
            //关闭线程池
            fixedThreadPool.shutdown();
        }

    }

3.3 编写分片下载的线程DownloaderTask

package com.sysg.file.core;

import com.sysg.file.constant.FileConstant;
import com.sysg.file.utils.HttpUtil;
import com.sysg.file.utils.LogUtil;
import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.util.concurrent.CountDownLatch;

/**
 * 分块下载任务
 */
public class DownloaderTask implements Runnable {

    /**
     * url
     */
    private final String url;

    /**
     * 下载的起始位置
     */
    private final long startPos;

    /**
     * 下载的结束位置
     */
    private final long endPos;

    /**
     * 标识当前下载块
     */
    private final int part;

    /**
     * 程序计数器
     */
    private 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;
    }

    @Override
    public void run() {
        //获取文件名
        String httpFileName = HttpUtil.getHttpFileName(url);
        //下载路径 + 分块文件名
        httpFileName = FileConstant.DOWNLOAD_PATH + httpFileName + ".temp" + part;
        //获取分块下载的链接
        HttpURLConnection httpURLConnection = HttpUtil.getHttpURLConnection(url, startPos, endPos);
        try(
                InputStream inputStream = httpURLConnection.getInputStream();
                BufferedInputStream bis = new BufferedInputStream(inputStream);
                RandomAccessFile randomAccessFile = new RandomAccessFile(httpFileName,"rw");

        ){
            byte[] buffer = new byte[FileConstant.BYTE_SIZE];
            int len = -1;
            //循环读取数据
            while((len = bis.read(buffer)) != -1){
                //一秒内下载数据之和,通过原子类进行操作
                DownloadInfoThread.downSize.add(len);
                randomAccessFile.write(buffer,0,len);
            }
        } catch (FileNotFoundException e) {
            LogUtil.error("下载文件不存在:{}",url);
        } catch (Exception e) {
            LogUtil.error("url出现异常,此url有问题,请换一个试试");
        } finally {
            countDownLatch.countDown();
            //关闭链接
            httpURLConnection.disconnect();
        }
    }
}

3.4 合并文件

/**
     * 合并文件
     *
     * @param fileName 文件名称
     * @return boolean 文件合并是否成功
     */
    public boolean mergeFile(String fileName){
        LogUtil.info("开始合并文件:{}",fileName);
        byte[] buffer = new byte[FileConstant.BYTE_SIZE];
        int len = -1;
        try (RandomAccessFile accessFile = new RandomAccessFile(FileConstant.DOWNLOAD_PATH + fileName, "rw")){
            for (int i = 0; i < FileConstant.THREAD_NUM; i++) {
                try (
                        FileInputStream fileInputStream = new FileInputStream(FileConstant.DOWNLOAD_PATH + fileName + ".temp" + i);
                        BufferedInputStream bis = new BufferedInputStream(fileInputStream);
                ) {
                    while ((len = bis.read(buffer)) != -1){
                        accessFile.write(buffer,0,len);
                    }
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
            LogUtil.info("结束合并文件:{}",fileName);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

3.5 清理临时文件

/**
     * 清理临时文件
     *
     * @param fileName 文件名称
     */
    public void clearTemp(String fileName){
        for (int i = 0; i < FileConstant.THREAD_NUM; i++) {
            //删除临时文件
            File file = new File(FileConstant.DOWNLOAD_PATH,fileName + ".temp" + i);
            if(file.exists()){
                file.delete();
            }
        }

    }

3.6 编写main方法

	public static void main(String[] args) {
        String url = "https://dldir1.qq.com/qqfile/qq/PCQQ9.7.9/QQ9.7.9.29059.exe";
        new Downloader().downloader(url);
    }

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

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

相关文章

基于SpringBoot+vue的简历系统设计和实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

ESXi 7.0 U3m HPE (慧与) 定制版 OEM Custom Installer CD

VMware ESXi 7.0 Update 3m - 领先的裸机 Hypervisor (All OEM Customized Installer CDs) ESXi 7.0 U3m Standard (标准版) ESXi 7.0 U3m Dell (戴尔) 定制版 OEM Custom Installer CD ESXi 7.0 U3m HPE (慧与) 定制版 OEM Custom Installer CD ESXi 7.0 U3m Lenovo (联想) 定…

基于SSM的养老机构信息管理系统设计与实现

摘 要 随着我国老年人逐渐增加&#xff0c;老人们的子女数量减少&#xff0c;工作时间过长无暇照顾父母&#xff0c;导致养老院和护工需求量大幅上涨。伴随我国生活水平提高的同时对老年人的护工人员的要求也越来越高。根据以上要求关于养老院有很多的信息需要进行管理&#…

Zero-Shot, One-Shot, and Few-Shot Learning概念介绍

导语 本文将介绍零样本学习、一次样本学习和少样本学习的概念&#xff0c;它们使得机器学习模型能够在仅有有限数量的示例情况下对对象或模式进行分类和识别。 在机器学习中&#xff0c;我们通常需要大量的训练数据来训练模型&#xff0c;以便它能够准确地识别和分类新的输入…

数据挖掘知识与学习方向

数据挖掘是一门涉及多个学科的交叉学科&#xff0c;需要掌握的知识点也比较多。以下是一些数据挖掘的基础知识和学习方向&#xff1a; 数据库&#xff1a;需要掌握 SQL 语言和数据库设计&#xff0c;以便能够有效地管理和处理数据。 统计学&#xff1a;需要掌握基本的统计学知…

硬盘初始化后数据还能恢复吗?硬盘被初始化怎么恢复数据

现今热门的数据恢复话题之一便是硬盘被初始化后如何恢复数据。或许许多人都遭遇过这一问题&#xff0c;往往因为误操作或不小心&#xff0c;导致硬盘数据被不可逆地清除。所以&#xff0c;为帮助广大用户避免数据丢失的情况&#xff0c;本文将为大家介绍如何恢复被初始化的硬盘…

基于注解的Spring(IOC+AOP)

目录 这是基于黑马Spring的笔记 写再前面 开始 Component(valuebean的名称) componet衍生出的3个注解 Bean内部的属性进行注入 非自定义的Bean管理 使用配置类完全替代XML配置文件 配置类中的注解 spring中的其他注解&#xff08;偶尔会用到) Spring注解的解析原理 sp…

Java Supervisor RPC2 接口对接

1.引入xmlrpc-client 如果是C#语言&#xff0c;请参考《C#对接supervisor XML-RPC API 实现进程控制》 如何安装Supervisor&#xff0c;请参考《Linux进程守护—Supervisor&#xff08;ubuntu&#xff09;》 如果是Maven项目&#xff0c;则在pom.xml引入jar包 <dependenc…

详解CSS中的flex布局

详解CSS中的flex布局 1、概念2、容器属性2.1 flex-direction2.2 flex-wrap2.3 flew-flow2.4 justify-content2.5 align-items2.6 align-content 3、元素属性3.1 order3.2 flex-grow3.3 flex-shrink3.4 flex-basis3.5 flex3.6 align-self 1、概念 弹性盒子&#xff08;display: …

如何系列 JMeter如何录制脚本

文章目录 方式1. 手动编写2. JMeter自带录制功能3. Fiddler录制4. Badboy录制5. Blazemeter录制 总结和使用感受 方式 1. 手动编写 最原始的方式&#xff0c;在线程组中根据研发提供的接口文档和浏览器的Network请求一个个手动录入&#xff0c;它可以提供更大的灵活性和控制力…

PostgreSQL 中的虚拟文件描述符

由于每个操作系统限制了一个进程能打开的文件数&#xff08;例如&#xff1a;ubuntu 为1024&#xff09;&#xff0c;因此进程能获得的文件描述符是有限的。对于经常需要打开许多文件的数据库进程来说&#xff0c;很容易会超过操作系统对于文件描述符数量的限制。 为解决这个问…

如何写好一份解决方案

1、前言 我们在日常工作中会不可避免要去编写各种方案&#xff0c;如技术方案、建设方案、项目建议书、实施方案、规划方案、解决方案等。 我们去浏览华为、阿里、腾讯、IBM、海尔等公司的官网&#xff0c;可以看到在首页比较显眼的位置&#xff0c;都有解决方案的入口&#…

Postcat X APISIX 合作插件 :一键同步,轻松配置到 APISIX

近日&#xff0c;云流科技&#xff08;广州&#xff09;有限公司&#xff08;简称“Eolink”&#xff09;旗下的开源 API 管理工具 Postcat 和深圳支流科技有限公司&#xff08;简称“API7 支流科技”&#xff09;在各自擅长的领域携手合作&#xff0c;推出了 Postcat & Ap…

R语言 tidyverse系列学习笔记(系列4)PlantGrowth - percentage table

本篇学习数据分析&#xff0c; Excel 表格制作 Task&#xff1a; 创建一个 行 百分比 表格 row percentage table 先看一下 PlantGrowth 数据集 library(dplyr)data("PlantGrowth") view(PlantGrowth)给数据集新加一列 weight_cat &#xff0c;并用 case_when 自定…

【React】: React的生命周期

概述 生命周期的每个阶段总是伴随着一些方法的调用&#xff0c;这些方法就是生命周期的钩子函数 钩子函数的作用&#xff1a;为开发人员在不同操作阶段提供了十几 只有 类组件 才有生命周期 生命周期的图片&#xff1a; 同时有&#xff1a; 1.1 创建时 的生命周期执行顺序 编写…

计划任务使用介绍

作者:lly 文章目录 前言一、使用说明1.1 发布模型1.2 创建并设置计划任务1.3 开启计划任务1.4 管理计划任务 二、结语 前言 iServer 11i(2023)对于处理自动化服务新增计划任务功能&#xff0c;该功能支持定时触发和监听文件变化触发执行模型&#xff0c;因此计划任务适用于以下…

Vite构建工具下Tinymce踩坑指南

背景 "vue": "^3.2.41","vite": "^3.2.3","tinymce": "^6.4.2","tinymce/tinymce-vue": "^5.1.0",坑1: 找不到zh-Hans.js等配置的文件 使用Tinymce需要配置中文汉化包、样式等。这些文件都是…

安全问题我们需要重视,立刻升级fastjson2

一、前言 小伙伴大家好&#xff0c;我是开源字节快速开发平台的作者。fastjson2.0 是fastjson的重要升级&#xff0c;目标是为下一个十年提供一个高性能的JSON库&#xff0c;同一套API支持JSON/JSONB两种协议。 近期fastjson 再报安全漏洞&#xff0c;直接给我们发送了高危告…

FastReport 2023.2新版, Delphi 和 Lazarus 核心产品将统一整合, 一起来看!

FastReport 是功能齐全的报表控件&#xff0c;可以帮助开发者可以快速并高效地为.NET&#xff0c;VCL&#xff0c;COM&#xff0c;ActiveX应用程序添加报表支持&#xff0c;由于其独特的编程原则&#xff0c;现在已经成为了Delphi平台最优秀的报表控件&#xff0c;支持将编程开…

风景类Midjouney prompt提示词

稳定输出优美风景壁纸的Midjouney prompt提示词。 1\在夏夜&#xff0c;有淡蓝色的星空&#xff0c;海边&#xff0c;流星&#xff0c;烟花&#xff0c;海滩上全是蓝色的玫瑰和绿色的植物&#xff0c;由Ivan Aivazovsky和Dan Mumford&#xff0c;趋势在cgsociety&#xff0c;柔…