go服务分片下载文件+分片上传文件

目录

先看效果

全代码实现

注意

完整输出


如有帮助,欢迎留下足迹哦!

先看效果

待上传的源文件(这里以一个140多M的为例)

上传后服务端下载完成的文件(result.exe):

全代码实现

定义主要路径、分片大小等

const (
	chunkSize = 10 * 1024 * 1024 // 分片大小为10MB
	baseURL   = "http://localhost:9001/upload/chunk"

	fileToUpload = "D:\\debug_path\\src\\jdk-11.0.20_windows-x64_bin.exe" // 待上传的大文件
	uploadDir    = "D:\\debug_path\\dst\\"                                // 存储临时文件的目录
	downloadDir  = "D:\\debug_path\\dst"                                  // 结果文件的目录
)

启动一个服务并直接执行上传

func main() {
	go func() {
		http.HandleFunc("/upload/chunk", handleChunkUpload)
		http.ListenAndServe("localhost:9001", nil)
	}()

	time.Sleep(time.Second * 2)
	file, err := os.Open(fileToUpload)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	err = chunkedUpload(file, filepath.Base(fileToUpload))
	if err != nil {
		log.Fatal(err)
	}

	select {}
}

分片上传核心逻辑

这里用go程序模拟,可由包括界面在内的常见手段发起:

func chunkedUpload(file *os.File, fileName string) error {
	md5hash := md5.New()
	
	totalSize, err := io.Copy(md5hash, file) // 148260984
	if err != nil {
		return err
	}

	completeMD5 := fmt.Sprintf("%x", md5hash.Sum(nil)) 

	_, err = file.Seek(0, 0)
	if err != nil {
		fmt.Println("seek err: ", err.Error())
		return err
	}

	reader := bufio.NewReader(file)

	var transferSize int       // 已传输的大小
	var currentChunk int64 = 1 // 当前是第几片
	buf := make([]byte, chunkSize)
	for {
		var n int
		n, err = reader.Read(buf)
		// 已读完所有
		if n == 0 && err == io.EOF {
			break
		}

		if err != nil {
			return err
		}

		body := bytes.NewReader(buf[:n])
		req, _ := http.NewRequest("POST", baseURL, body)
		req.Header.Set("Content-Type", "application/octet-stream")
		req.Header.Set("X-File-Name", fileName)
		req.Header.Set("X-Chunk-Number", fmt.Sprintf("%d", currentChunk))                      // 当前第几片
		req.Header.Set("X-Total-Chunks", fmt.Sprintf("%d", (totalSize+chunkSize-1)/chunkSize)) // 总分片数量
		req.Header.Set("X-File-MD5", completeMD5)
		req.Header.Set("X-File-Size", fmt.Sprintf("%d", totalSize))

		client := &http.Client{}
		resp, err := client.Do(req)
		if err != nil || resp.StatusCode != http.StatusOK {
			log.Printf("[%d] Error uploading chunk %d: %v.\n", resp.StatusCode, currentChunk, err)
			return err
		}

		defer resp.Body.Close()

		transferSize += n

		fmt.Printf("---> %d request done. currentChunk=%d, transferSize=%d, total=%d.\n", currentChunk, n, transferSize, totalSize)
		currentChunk++
	}

	return nil
}

服务端分片接收和下载

func handleChunkUpload(w http.ResponseWriter, r *http.Request) {
	fileName := r.Header.Get("X-File-Name")
	chunkNumberStr := r.Header.Get("X-Chunk-Number")
	totalChunksStr := r.Header.Get("X-Total-Chunks")
	expectedMD5 := r.Header.Get("X-File-MD5")
	totalSizeStr := r.Header.Get("X-File-Size")

	fmt.Printf("正在处理上传的文件:fileSize=%s, chunk=%s/%s\n", totalSizeStr, chunkNumberStr, totalChunksStr)

	totalSize, err := strconv.Atoi(totalSizeStr)
	if err != nil {
		http.Error(w, "Invalid file size", http.StatusBadRequest)
		return
	}

	chunkNumber, err := strconv.Atoi(chunkNumberStr)
	if err != nil {
		http.Error(w, "invalid chunk number", http.StatusBadRequest)
		return
	}

	totalChunks, err := strconv.Atoi(totalChunksStr)
	if err != nil {
		http.Error(w, "invalid total chunks", http.StatusBadRequest)
		return
	}

	tempFileName := filepath.Join(uploadDir, fmt.Sprintf("%s.chunk.%d", fileName, chunkNumber))
	fmt.Println("当前第 ", chunkNumber, "片  临时文件名:", tempFileName)

	tmpFile, err := os.Create(tempFileName)
	if err != nil {
		fmt.Printf("failed to create temp file: %s\n", err.Error())
		http.Error(w, fmt.Sprintf("failed to create temp file: %s", err.Error()), http.StatusInternalServerError)
		return
	}
	defer tmpFile.Close()

	md5hash := md5.New()

	_, err = io.Copy(io.MultiWriter(tmpFile, md5hash), r.Body)
	if err != nil {
		fmt.Printf("failed to copy tmpFile: %s\n", err.Error())
		http.Error(w, "failed to write chunk data", http.StatusInternalServerError)
		return
	}

	actualMD5 := fmt.Sprintf("%x", md5hash.Sum(nil))

	fmt.Printf("actualMD5=%s, expectedMD5=%s. ==%v\n", actualMD5, expectedMD5, actualMD5 == expectedMD5)

	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "%s", "success")

	if chunkNumber == totalChunks {
		go merge(totalSize, totalChunks, fileName, expectedMD5)
	}

	w.WriteHeader(http.StatusOK)
}

分片传输完成后,服务端完成合并和校验

func merge(fileSize, totalChunks int, fileName, expectedMD5 string) {
	fmt.Println("所有已传完,准备合并。 totalChunks = ", totalChunks)

	outputPath := path.Join(downloadDir, "result.exe")
	outputFile, err := os.Create(outputPath)
	if err != nil {
		fmt.Println("create failed: ", err)
		return
	}
	defer outputFile.Close()

	var filePaths []string

	//  生成每个文件的路径
	for i := 1; i <= totalChunks; i++ {
		tempName := fmt.Sprintf("%s.chunk.%d", fileName, i)
		filePath := filepath.Join(uploadDir, tempName)
		filePaths = append(filePaths, filePath)
	}

	for i, filePath := range filePaths {
		fmt.Printf("第 %d 个临时文件路径为:%s.\n", i+1, filePath)
		inputFile, openErr := os.Open(filePath)
		if openErr != nil {
			fmt.Println("打开失败:", openErr)
			return
		}
		defer inputFile.Close()

		_, copyErr := io.Copy(outputFile, inputFile)
		if copyErr != nil {
			fmt.Println("copy 失败:", copyErr)
			return
		}

		fmt.Printf("第 %d 个文件已合并.\n", i+1)

		info, statErr := outputFile.Stat()
		if statErr != nil {
			fmt.Println("获取文件信息失败:", statErr)
			return
		}

		fmt.Printf("当前outputFile size=%d, fileSize=%d.\n", info.Size(), fileSize)
	}

	outputFile.Seek(0, 0)
	actualMD5, err := GetMd5ByFile(outputFile)
	if err != nil {
		fmt.Println("获取文件md5失败:", err)
		return
	}

	if actualMD5 != expectedMD5 {
		fmt.Println("校验失败:", actualMD5, expectedMD5)
		return
	}

	info, err := outputFile.Stat()
	if err != nil {
		fmt.Println("获取文件信息失败:", err)
		return
	}

	fmt.Printf("合并完成。文件md5=%s. resultSize=%d, fileSize=%d.\n", actualMD5, info.Size(), fileSize)
}

工具函数

func GetMd5ByFile(file *os.File) (string, error) {

	md5Hash := md5.New()
	if _, err := io.Copy(md5Hash, file); err != nil {
		return "", err
	}

	hashInBytes := md5Hash.Sum(nil)
	file.Seek(0, 0)
	return fmt.Sprintf("%x", hashInBytes), nil
}

注意

1、在完成合并后,临时文件可删除,尤其是在并发时注意删除临时文件的时机,不能删除的过早,可自行添加删除逻辑。

2、基于代码中双方的数据,服务端可添加缓存记

完整输出

正在处理上传的文件:fileSize=148260984, chunk=1/15
当前第  1 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.1
actualMD5=2bcc1c1a609a20c15fe8b0d64874e3b4, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:45 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 1 request done. currentChunk=10485760, transferSize=10485760, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=2/15
当前第  2 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.2
actualMD5=1c2fbb73080cb86db44f2814078d2609, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:45 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 2 request done. currentChunk=10485760, transferSize=20971520, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=3/15
当前第  3 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.3
actualMD5=f13fe6748c5048e8d21635186e8ddd54, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:46 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 3 request done. currentChunk=10485760, transferSize=31457280, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=4/15
当前第  4 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.4
actualMD5=507bac5d8884abe68098360414ae3c47, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:46 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 4 request done. currentChunk=10485760, transferSize=41943040, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=5/15
当前第  5 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.5
actualMD5=92e4cf595991d77330c69a7d91052783, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:46 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 5 request done. currentChunk=10485760, transferSize=52428800, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=6/15
当前第  6 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.6
actualMD5=74b84f9ba86a6b4648ce9f06026f9819, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:47 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 6 request done. currentChunk=10485760, transferSize=62914560, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=7/15
当前第  7 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.7
actualMD5=ca6d4fae23ee61b54a5c18cdc1137702, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:47 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 7 request done. currentChunk=10485760, transferSize=73400320, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=8/15
当前第  8 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.8
actualMD5=e24234da9d55c4e577cdfa9ab932170b, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:47 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 8 request done. currentChunk=10485760, transferSize=83886080, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=9/15
当前第  9 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.9
actualMD5=c7697c3cbd8a145bb921704bdc83692b, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:48 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 9 request done. currentChunk=10485760, transferSize=94371840, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=10/15
当前第  10 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.10
actualMD5=bc647d44a4b094e00c27d67c521f63d1, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:48 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 10 request done. currentChunk=10485760, transferSize=104857600, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=11/15
当前第  11 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.11
actualMD5=57edc6d0e665279325d25d4238085475, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:48 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 11 request done. currentChunk=10485760, transferSize=115343360, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=12/15
当前第  12 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.12
actualMD5=74c7b12cdf056b8812af19bdf96528d6, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:49 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 12 request done. currentChunk=10485760, transferSize=125829120, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=13/15
当前第  13 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.13
actualMD5=9c6e375efa255425828d16235210b956, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:49 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 13 request done. currentChunk=10485760, transferSize=136314880, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=14/15
当前第  14 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.14
actualMD5=d118498b6056355e84ea46b35369d988, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
2023/12/09 14:45:49 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 14 request done. currentChunk=10485760, transferSize=146800640, total=148260984.
正在处理上传的文件:fileSize=148260984, chunk=15/15
当前第  15 片  临时文件名: D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.15
actualMD5=4b0c19522a6b242ddfed4b4d6c5dda54, expectedMD5=ade9fb7e919b2642191de90325b66cc6. ==false
所有已传完,准备合并。 totalChunks =  15
2023/12/09 14:45:50 http: superfluous response.WriteHeader call from main.handleChunkUpload (server.go:78)
---> 15 request done. currentChunk=1460344, transferSize=148260984, total=148260984.
第 1 个临时文件路径为:D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.1.
第 1 个文件已合并.
当前outputFile size=10485760, fileSize=148260984.
第 2 个临时文件路径为:D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.2.
第 2 个文件已合并.
当前outputFile size=20971520, fileSize=148260984.
第 8 个临时文件路径为:D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.8.
第 8 个文件已合并.
当前outputFile size=83886080, fileSize=148260984.
第 9 个临时文件路径为:D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.9.
第 9 个文件已合并.
当前outputFile size=94371840, fileSize=148260984.
第 10 个临时文件路径为:D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.10.
第 10 个文件已合并.
当前outputFile size=104857600, fileSize=148260984.
第 11 个临时文件路径为:D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.11.
第 11 个文件已合并.
当前outputFile size=115343360, fileSize=148260984.
第 12 个临时文件路径为:D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.12.
第 12 个文件已合并.
当前outputFile size=125829120, fileSize=148260984.
第 13 个临时文件路径为:D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.13.
第 13 个文件已合并.
当前outputFile size=136314880, fileSize=148260984.
第 14 个临时文件路径为:D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.14.
第 14 个文件已合并.
当前outputFile size=146800640, fileSize=148260984.
第 15 个临时文件路径为:D:\debug_path\dst\jdk-11.0.20_windows-x64_bin.exe.chunk.15.
第 15 个文件已合并.
当前outputFile size=148260984, fileSize=148260984.
合并完成。文件md5=ade9fb7e919b2642191de90325b66cc6. resultSize=148260984, fileSize=148260984.

录每次接收到后的文件位置,便于后续处理;同时上传方也每次都能知道上传的进度。

完整输出

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

WebRTC视频会议/视频客服系统EasyRTC进入会议室密码验证的开发与实现

基于WebRTC技术的EasyRTC视频会议系统&#xff0c;建设目标是让用户随时随地、快捷方便地进行视频会议&#xff0c;并根据行业需求有针对性地提供多样化、个性化功能&#xff0c;该系统是覆盖全球的实时音视频开发平台&#xff0c;支持一对一、一对多等视频通话&#xff0c;极大…

第二证券:行业术语解读:CPO概念是什么意思?

cpo概念又名共封装光学概念&#xff0c;它是指把硅光模块和CMOS芯片用高级封装的方法耦合在背板PCB上&#xff0c;从而在成本、功耗和尺度上都进一步提升数据中心使用中的光互联技能等相关上市公司组成的概念。 概念股&#xff0c;并不特指于某一支股&#xff0c;而是一个选股话…

Git 常用命令详解及如何在IDEA中操作

文章目录 前言发现宝藏一、初识Git1.Git概述2. Git的功能3. Git运行图示 二、Git下载安装三、Git 代码托管服务1.常用的 Git 代码托管服务2.使用码云代码托管服务 四、Git 常用命令1.Git 全局设置2.获取Git 仓库3.工作区、暂存区、版本库 概念4.Git 工作区中文件的两种状态5.本…

【STM32CubeMX串口通信详解】USART1 -- DMA发送 + DMA空闲中断 接收不定长数据

文章目录&#xff1a; 前言 一、准备工作 1、接线 2、新建工程 二、CubeMX的配置 1、USART1 配置 异步通信 2、通信协议参数 3、打开DMA发送、接收 三、发送操作、代码解释 四、printf 重定向到USART1 五、接收代码的编写 1、定义一个结构体变量&a…

一款好用的开源思维导图软件 docker部署教程

目录 Simple mind map简介 Simple mind map特点 1.拉取镜像 2.创建并启动容器 方式1&#xff1a;docker启动 方式2&#xff1a;docker compose启动 3.使用 4.源码地址 Simple mind map简介 .一个 Web 思维导图&#xff0c;基于思维导图库、Vue2.x、ElementUI 开发&#…

聚观早报 |一加Ace 3鸣沙金开售;地平线征程6将发布

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 1月16日消息 一加Ace 3鸣沙金开售 地平线征程6将发布 互动短剧《谍影成双》首曝PV 台积电发布四季度财报 保时捷…

day2:TCP、UDP网络通信模型

思维导图 机械臂实现 #include <head.h> #define SER_POTR 8899 #define SER_IP "192.168.125.223" int main(int argc, const char *argv[]) {//创建套接字int cfdsocket(AF_INET,SOCK_STREAM,0);if(cfd-1){perror("");return -1;}//链接struct so…

java版直播商城平台规划及常见的营销模式 电商源码/小程序/三级分销+商城 免 费 搭 建

鸿鹄云商 B2B2C产品概述 【B2B2C平台】&#xff0c;以传统电商行业为基石&#xff0c;鸿鹄云商支持“商家入驻平台自营”多运营模式&#xff0c;积极打造“全新市场&#xff0c;全新 模式”企业级B2B2C电商平台&#xff0c;致力干助力各行/互联网创业腾飞并获取更多的收益。从消…

主流视频压缩格式

主流的视频压缩格式有很多&#xff0c;它们各自适用于不同的应用场景&#xff0c;如在线流媒体、广播、存档等。 以下是一些广泛使用的视频压缩格式&#xff1a; H.264/AVC (Advanced Video Coding): 目前最为广泛使用的视频压缩标准之一&#xff0c;兼容性极佳&#xff0c;广泛…

打造独特体验:App封装的新纪元

在数字化的浪潮中&#xff0c;应用程序已成为我们日常生活中不可或缺的一部分。对于开发者而言&#xff0c;创造一个既能吸引用户又能提供独特价值的免费安卓应用是一项挑战。这就是封装&#xff08;Wrapping&#xff09;技术发挥作用的地方。通过封装&#xff0c;开发者可以将…

二十四、同域名下JSESSIONID重叠导致退出

同域名下JSESSIONID重叠导致退出 近期在开发项目的时候发现,如果同域名的情况下,如果把一个单页面无登录系统嵌套进入另外一个系统,那么会出现相互退出的问题。 思考解决方案 一、清除掉嵌套的系统的JSESSIONID,意思就是嵌套系统不设置JSESSIONID 1找寻出问题接口 在无痕…

【文本到上下文 #6】高级词嵌入:Word2Vec、GloVe 和 FastText

一、说明 欢迎来到“完整的 NLP 指南。到目前为止&#xff0c;我们已经探索了自然语言处理的基础知识、应用程序和挑战。我们深入研究了标记化、文本清理、停用词、词干提取、词形还原、词性标记和命名实体识别。我们的探索包括文本表示技术&#xff0c;如词袋、TF-IDF 以及词嵌…

【Qt之Quick模块】8. Quick基础、布局管理、布局管理器

1. 前言 Qt Quick编程&#xff0c;提供了多种布局方式。 如&#xff0c;静态布局&#xff0c;可以使用组件的x、y属性进行设置&#xff0c;或者进行绑定。 还可以使用锚anchors进行布局。 此外&#xff0c;还可以使用定位器以及定位管理器为多组件进行布局。 但使用布局管理器…

【数学建模美赛M奖速成系列】数据可视化方法(一)

数据可视化方法 写在前面山脊图优点缺点实现matlabpython 气泡矩阵图实现matlabpython 后续 写在前面 最近开始更新一个新的系列科研绘图&#xff0c;在同一个竞赛下&#xff0c;大家都近乎相同的解题思路下。之所以能出现一等二等三等奖的区别很大部分都在于结果的可视化&…

实战 php 使用 wkhtmltopdf 生成pdf的全过程

公司里边有生成pdf报告的业务需求,之前有过尝试用tcpdf,直接生成的pdf的过程,但是pdf报告的内容数据,根据不同内容的变化,都是各种各样的bug,一直处理修修补补的状态,让后台开发人员很是头疼. 经过思索和甄选,总结出我们的业务中是由于样式不可控导致的,当时从逻辑上就思考到用…

半监督学习 - 三元组学习(Triplet Learning)

什么是机器学习 三元组学习&#xff08;Triplet Learning&#xff09;是半监督学习中一种用于学习有用表示的方法。它通常用于学习数据中的相似性关系&#xff0c;尤其在人脸识别、图像检索等领域中得到广泛应用。三元组学习是通过构造三元组&#xff08;triplet&#xff09;来…

Java NIO (一)简介(备份)

1 NIO简介 在1.4版本之前&#xff0c;Java NIO类库是阻塞IO&#xff0c;从1.4版本开始&#xff0c;引进了新的异步IO库&#xff0c;被称为Java New IO类库&#xff0c;简称为Java NIO。New IO类库的目的 就是要让Java支持非阻塞IO。 Java NIO类库包含三个核心组件&#xff1a; …

AGV磁导航传感器CNS-MGS-160N安装与调试方法与注意事项

AGV磁导航传感器CNS-MGS-160N通过上位机给传感器下发岔路选择指令&#xff0c;传感器即⾃动屏蔽⾮选择⽅向的磁条&#xff0c;使AGV平滑过弯⽆抖动&#xff0c;可通过配置软件设置传感器的检测灵敏度&#xff0c;适应不同的安装⾼度下的检测要求&#xff0c;其通过输出1mm⾼精度…

行测:国考省考行测:语句表达,位置分析,语句分析,语句填空

国考省考行测&#xff1a;语句表达 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff0c;我讲一起屡屡申论和行测的重要知识点 遇到…

STM32 CubeIDE 使用 CMSIS-DAP烧录 (方法2--外部小工具)

前言&#xff1a; 本篇所用方法&#xff0c;需要借助一个外部的工具小软件。 优点&#xff1a;烧录更稳定; 缺点&#xff1a;不能在线仿真调试。 下面链接&#xff0c;是另一种方法&#xff1a;修改CubeIDE调试文件。能在CubeIDE直接烧录、仿真&#xff0c;但不稳定。…