当前位置: 首页 > news >正文

大文件上传

上图就是大致的流程


一、标题图片

  1. 上传课程的标题图片

  1. Ajax发送请求到后端

  1. 后端接收到图片使用IO流去保存图片,返回图片的信息对象

  1. JS回调函数接收对象通过$("元素id").val(值),方式给页面form表达img标签src属性值,达到上传图片并回显

二、大文件上传(流媒体、音频、.zip文件等)

  1. 页面div点击事件,选中一个视频文件

  1. JS判断文件大小,判断文件类型是否合法

3.JS循环切片,计算总片数,计算每一片的起始位置,循环上传

4.发送Ajax转给后端切片

5.后端接收并创建临时目录存放

    /*** 分片上传,前端调用此方法* @param request* @param guid* @param chunk* @param file* @return*/@PostMapping("/uploadSlice")@ResponseBodypublic ResponseResult<?> uploadSlice2(HttpServletRequest request, @RequestParam("guid") String guid,@RequestParam("chunk") Integer chunk,@RequestParam("file") MultipartFile file) {if (this.uploadSlice(request, guid, chunk, file)){return ResponseResultUtils.genResult("上传成功","");}else{return ResponseResultUtils.genErrorResult("上传失败");}}/*** 分片上传的具体方法* @param request* @param guid* @param chunk* @param file* @return*/private boolean uploadSlice(HttpServletRequest request, String guid, Integer chunk, MultipartFile file) {try {boolean isMultipart = ServletFileUpload.isMultipartContent(request);logger.info("isMultipart = {}",isMultipart);if (isMultipart) {if (chunk == null){chunk = 0;}// 临时目录用来存放所有分片文件String tempFileDir =  rootFilePath + bigPath + guid;File parentFileDir = new File(tempFileDir);if (!parentFileDir.exists()) {parentFileDir.mkdirs();}logger.info("接到上传的分片文件,{},{},{}",guid,chunk,tempFileDir);// 分片处理时,前台会多次调用上传接口,每次都会上传文件的一部分到后台File tempPartFile = new File(parentFileDir, guid + "_" + chunk + ".part");FileUtils.copyInputStreamToFile(file.getInputStream(), tempPartFile);}} catch (Exception e) {return false;}return true;}

6.计数器变量值=总片数时,发送合并请求

7.后端合并

/*** 分片文件合并,前端调用此方法* @param guid* @param fileName* @return*/@RequestMapping("/uploadMerge")@ResponseBodypublic ResponseResult<?> uploadMerge2(@RequestParam("guid") String guid, @RequestParam("fileName") String fileName) {// 得到 destTempFile 就是最终的文件SpaceImage image = this.uploadMerge(guid, fileName);if(null != imageSpaceImage){return ResponseResultUtils.genResult(imageSpaceImage,"合并成功");}else{return ResponseResultUtils.genErrorResult("合并文件失败");}}private ImageSpaceImage uploadMerge(String guid, String fileName){SpaceImage image = mergeFile(guid, fileName);//此处需要注意,OSS需要再次切片上传,但minIO是不用得,它默认5M超过就会自动切片String path = "";//移除文件poolTaskExecutor.execute(() -> {com.eyang.ecpp.utils.FileUtils.deleteFile(rootFilePath+bigPath);});return imageSpaceImage;}private ImageSpaceImage mergeFile(String guid, String fileName) {logger.info("接到上传的分片文件合并请求,{},{}",guid,fileName);try {String sName = fileName.substring(fileName.lastIndexOf("."));//时间格式化格式Date currentTime = new Date();SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");//获取当前时间并作为时间戳String timeStamp = simpleDateFormat.format(currentTime);//拼接新的文件名String newName = timeStamp + sName;simpleDateFormat = new SimpleDateFormat("yyyyMM");String tempPath = rootFilePath + bigPath+guid;String margePath = rootFilePath + bigPath+simpleDateFormat.format(currentTime);File parentFileDir = new File(tempPath);if (parentFileDir.isDirectory()) {File destTempFile = new File(margePath, newName);if (!destTempFile.exists()) {//先得到文件的上级目录,并创建上级目录,在创建文件destTempFile.getParentFile().mkdir();destTempFile.createNewFile();}for (int i = 0; i < Objects.requireNonNull(parentFileDir.listFiles()).length; i++) {File partFile = new File(parentFileDir, guid + "_" + i + ".part");FileOutputStream destTempfos = new FileOutputStream(destTempFile, true);//遍历"所有分片文件"到"最终文件"中FileUtils.copyFile(partFile, destTempfos);destTempfos.close();}// 删除临时目录中的分片文件FileUtils.deleteDirectory(parentFileDir);String[] resultArr = FileStorageUtils.saveBigFile(Files.readAllBytes(Paths.get(destTempFile.getPath())), com.eyang.ecpp.utils.FileUtils.getExtension(destTempFile.getName()));ImageSpaceImage imageSpaceImage = new ImageSpaceImage();//第一个是组名 第二个是改后的文件名imageSpaceImage.setImgUrl(margePath+"/"+newName);//imageSpaceImage.setName(destTempFile.getName());imageSpaceImage.setName(fileName);return imageSpaceImage;//destTempFile.getAbsolutePath();}} catch (Exception e) {logger.error("切片文件合并,失败原因e:{}", e.getMessage());}return null;}

8.合并成功后进行转码

转码需要下载一个ffmpeg,下载完解压打开bin目录,打开电脑的环境变量往path中添加上bin的目录即可。

还需要引入依赖

 <!--  视频编码 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>

9.转码发送请求

10.后端转码

/*** 视频编码** @param absolutePath 绝对路径* @return {@link ResponseResult}<{@link ?}>*/@RequiresPermissions("cms:article:edit")@RequestMapping(value = "video/coding")@ResponseBodypublic ResponseResult<?> videoCoding(@RequestParam String absolutePath) {TranscodeConfig transcodeConfig = new TranscodeConfig();//设置视频封面transcodeConfig.setPoster("1");transcodeConfig.setTsSeconds("60");ResponseResult<?> transResult = TranscodeFileUtils.transCodeFile(absolutePath, transcodeConfig);String retUrl = "";if (null != transResult) {Map<String, Object> data = (Map<String, Object>) transResult.getData();if (null != data) {Map<String, Object> videoInfo = (Map<String, Object>) data.get("data");if (null != videoInfo) {retUrl = (String) videoInfo.get("m3u8");}}}return ResponseResultUtils.genResult(retUrl,"转码成功");}

视频转码配置实体类

package com.utils;public class TranscodeConfig {private String poster; // 截取封面的时间private String tsSeconds; // ts分片大小,单位是秒private String cutStart; // 视频裁剪,开始时间private String cutEnd; // 视频裁剪,结束时间public String getPoster() {return poster;}public void setPoster(String poster) {this.poster = poster;}public String getTsSeconds() {return tsSeconds;}public void setTsSeconds(String tsSeconds) {this.tsSeconds = tsSeconds;}public String getCutStart() {return cutStart;}public void setCutStart(String cutStart) {this.cutStart = cutStart;}public String getCutEnd() {return cutEnd;}public void setCutEnd(String cutEnd) {this.cutEnd = cutEnd;}public TranscodeConfig() {}public TranscodeConfig(String poster, String tsSeconds, String cutStart, String cutEnd) {this.poster = poster;this.tsSeconds = tsSeconds;this.cutStart = cutStart;this.cutEnd = cutEnd;}
}

转码工具类

public class TranscodeFileUtils {/*** 视频根路径*/private static String videoFolder= Global.getConfig("video.folder");private static final Logger LOGGER = LoggerFactory.getLogger(TranscodeFileUtils.class);public static ResponseResult<Map<String, Object>> transCodeFile(String filePath, TranscodeConfig transcodeConfig){try {// 按照日期生成子目录String today = DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDate.now());String s = Identities.uuid2();Path targetFolder = Paths.get(videoFolder, today, s);// 执行转码操作LOGGER.info("开始转码");FFmpegUtils.transcodeToM3u8(filePath, targetFolder.toString(), transcodeConfig);// 封装结果Map<String, Object> videoInfo = new HashMap<>();videoInfo.put("m3u8", String.join("\\", targetFolder.toString(), "index.m3u8"));videoInfo.put("poster", String.join("\\", targetFolder.toString(), "poster.jpg"));Map<String, Object> result = new HashMap<>();result.put("success", true);result.put("data", videoInfo);return ResponseResultUtils.genResult(result,"转码成功!");} catch (Exception e) {e.printStackTrace();}return null;}public static void main(String[] args){System.out.println(TranscodeFileUtils.transCodeFile("E:\\录屏\\shipin\\test.mp4", new TranscodeConfig("00:00:00.001","15","","")));}}

转码工具类

public class FFmpegUtils {private static final Logger LOGGER = LoggerFactory.getLogger(FFmpegUtils.class);// 跨平台换行符private static final String LINE_SEPARATOR = System.getProperty("line.separator");/*** 生成随机16个字节的AESKEY* @return*/private static byte[] genAesKey ()  {try {KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");keyGenerator.init(128);return keyGenerator.generateKey().getEncoded();} catch (NoSuchAlgorithmException e) {return null;}}/*** 在指定的目录下生成key_info, key文件,返回key_info文件* @param folder* @throws IOException*/private static Path genKeyInfo(String folder) throws IOException {// AES 密钥byte[] aesKey = genAesKey();// AES 向量String iv = Hex.encodeHexString(genAesKey());// key 文件写入Path keyFile = Paths.get(folder, "key");Files.write(keyFile, aesKey, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);// key_info 文件写入StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("key").append(LINE_SEPARATOR);                    // m3u8加载key文件网络路径stringBuilder.append(keyFile.toString()).append(LINE_SEPARATOR);    // FFmeg加载key_info文件路径stringBuilder.append(iv);                                            // ASE 向量Path keyInfo = Paths.get(folder, "key_info");Files.write(keyInfo, stringBuilder.toString().getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);return keyInfo;}/*** 指定的目录下生成 master index.m3u8 文件* @param file            master m3u8文件地址* @param indexPath            访问子index.m3u8的路径* @param bandWidth            流码率* @throws IOException*/private static void genIndex(String file, String indexPath, String bandWidth) throws IOException {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("#EXTM3U").append(LINE_SEPARATOR);stringBuilder.append("#EXT-X-STREAM-INF:BANDWIDTH=" + bandWidth).append(LINE_SEPARATOR);  // 码率stringBuilder.append(indexPath);Files.write(Paths.get(file), stringBuilder.toString().getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);}/*** 转码视频为m3u8* @param source                源视频* @param destFolder            目标文件夹* @param config                配置信息* @throws IOException* @throws InterruptedException*/public static void transcodeToM3u8(String source, String destFolder, TranscodeConfig config) throws IOException, InterruptedException {// 判断源视频是否存在if (!Files.exists(Paths.get(source))) {throw new IllegalArgumentException("文件不存在:" + source);}// 创建工作目录Path workDir = Paths.get(destFolder, "ts");Files.createDirectories(workDir);// 在工作目录生成KeyInfo文件Path keyInfo = genKeyInfo(workDir.toString());// 构建命令List<String> commands = new ArrayList<>();commands.add("ffmpeg");commands.add("-i");commands.add(source);                    // 源文件commands.add("-c:v");commands.add("libx264");                // 视频编码为H264commands.add("-c:a");commands.add("copy");                    // 音频直接copycommands.add("-hls_key_info_file");commands.add(keyInfo.toString());        // 指定密钥文件路径commands.add("-hls_time");commands.add(config.getTsSeconds());    // ts切片大小commands.add("-hls_playlist_type");commands.add("vod");                    // 点播模式commands.add("-hls_segment_filename");commands.add("%06d.ts");                // ts切片文件名称if (StringUtils.hasText(config.getCutStart())) {commands.add("-ss");commands.add(config.getCutStart());    // 开始时间}if (StringUtils.hasText(config.getCutEnd())) {commands.add("-to");commands.add(config.getCutEnd());        // 结束时间}commands.add("index.m3u8");                                                        // 生成m3u8文件// 构建进程Process process = new ProcessBuilder().command(commands).directory(workDir.toFile()).start();// 读取进程标准输出new Thread(() -> {try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {String line = null;while ((line = bufferedReader.readLine()) != null) {LOGGER.info(line);}} catch (IOException e) {}}).start();// 读取进程异常输出new Thread(() -> {try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {String line = null;while ((line = bufferedReader.readLine()) != null) {LOGGER.info(line);}} catch (IOException e) {}}).start();// 阻塞直到任务结束if (process.waitFor() != 0) {throw new RuntimeException("视频切片异常");}// 切出封面if (!screenShots(source, String.join(File.separator, destFolder, "poster.jpg"), config.getPoster())) {throw new RuntimeException("封面截取异常");}// 获取视频信息MediaInfo mediaInfo = getMediaInfo(source);if (mediaInfo == null) {throw new RuntimeException("获取媒体信息异常");}// 生成index.m3u8文件genIndex(String.join(File.separator, destFolder, "index.m3u8"), "ts/index.m3u8", mediaInfo.getFormat().getBitRate());// 删除keyInfo文件Files.delete(keyInfo);}/*** 获取视频文件的媒体信息* @param source* @return* @throws IOException* @throws InterruptedException*/public static MediaInfo getMediaInfo(String source) throws IOException, InterruptedException {List<String> commands = new ArrayList<>();commands.add("ffprobe");commands.add("-i");commands.add(source);commands.add("-show_format");commands.add("-show_streams");commands.add("-print_format");commands.add("json");Process process = new ProcessBuilder(commands).start();MediaInfo mediaInfo = null;try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {mediaInfo = new Gson().fromJson(bufferedReader, MediaInfo.class);} catch (IOException e) {e.printStackTrace();}if (process.waitFor() != 0) {return null;}return mediaInfo;}/*** 截取视频的指定时间帧,生成图片文件* @param source        源文件* @param file            图片文件* @param time            截图时间 HH:mm:ss.[SSS]* @throws IOException* @throws InterruptedException*/public static boolean screenShots(String source, String file, String time) throws IOException, InterruptedException {List<String> commands = new ArrayList<>();commands.add("ffmpeg");commands.add("-i");commands.add(source);commands.add("-ss");commands.add(time);commands.add("-y");commands.add("-q:v");commands.add("1");commands.add("-frames:v");commands.add("1");commands.add("-f");commands.add("image2");commands.add(file);Process process = new ProcessBuilder(commands).start();// 读取进程标准输出new Thread(() -> {try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {String line = null;while ((line = bufferedReader.readLine()) != null) {LOGGER.info(line);}} catch (IOException e) {}}).start();// 读取进程异常输出new Thread(() -> {try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {String line = null;while ((line = bufferedReader.readLine()) != null) {LOGGER.error(line);}} catch (IOException e) {}}).start();return process.waitFor() == 0;}}

11.转码成功返回m3u8文件的路径,放到页面input隐藏标签中

编码成功后执行回调函数,m3u8文件赋给_data变量如下图

使用$(#页面元素id).val(值),下图是把m3u8赋值给页面的input

12.最后页面form提交,保存视频转码后m3u8的路径。

相关文章:

大文件上传

上图就是大致的流程一、标题图片上传课程的标题图片Ajax发送请求到后端后端接收到图片使用IO流去保存图片&#xff0c;返回图片的信息对象JS回调函数接收对象通过$("元素id").val(值)&#xff0c;方式给页面form表达img标签src属性值&#xff0c;达到上传图片并回显二…...

Python每日一练(20230327)

目录 1. 最大矩形 &#x1f31f;&#x1f31f;&#x1f31f; 2. 反转链表 II &#x1f31f;&#x1f31f; 3. 单词接龙 II &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日…...

Centos7 升级内核到5.10mellanox 编译安装

升级5.10内核 #uname -r 重启后 进入新的内核 进入新的内核信息 直接查看是看不到gcc版本 5.10需要高版本gcc 才可以进行编译...

冯诺依曼,操作系统以及进程概念

文章目录一.冯诺依曼体系结构二.操作系统&#xff08;operator system&#xff09;三.系统调用和库函数四.进程1.进程控制块&#xff08;PCB&#xff09;2.查看进程3.系统相关的调用4.fork介绍&#xff08;并发引入&#xff09;五.总结一.冯诺依曼体系结构 计算机大体可以说是…...

7.网络爬虫—正则表达式详讲

7.网络爬虫—正则表达式详讲与实战Python 正则表达式re.match() 函数re.search方法re.match与re.search的区别re.compile 函数检索和替换检索&#xff1a;替换&#xff1a;findallre.finditerre.split正则表达式模式常见的字符类正则模式正则表达式模式量词正则表达式举例前言&…...

关于位运算的巧妙性:小乖,你真的明白吗?

一.位运算的概念什么是位运算&#xff1f;程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。位运算就是直接操作二进制数&#xff0c;那么有哪些种类的位运算呢&#xff1f;常见的运算符有与(&)、或(|)、异或(^)、…...

【Android车载系列】第5章 AOSP开发环境配置

1 硬件支持 建议空闲内存16G以上&#xff0c;同时硬盘400G以上 内存不够可以使用 Linux 的交换分区2 VMware Workstation安装 https://download3.vmware.com/software/wkst/file/VMware-workstation-full-16.1.1-17801498.exe2.1 Ubuntu镜像 http://mirrors.aliyun.com/ubun…...

个人时间管理网站—Git项目管理

&#x1f31f;所属专栏&#xff1a;献给榕榕&#x1f414;作者简介&#xff1a;rchjr——五带信管菜只因一枚&#x1f62e;前言&#xff1a;该专栏系为女友准备的&#xff0c;里面会不定时发一些讨好她的技术作品&#xff0c;感兴趣的小伙伴可以关注一下~&#x1f449;文章简介…...

2023最新ChatGPT整理的40道Java高级面试题

2023 年最火的就是 ChatGPT 了,很多同事使用他完成一些代码上的智能提示,也有人使用它发了财《「用ChatGPT年入百万!」各博主发布生财之道,网友:答辩搬运工》、《“躺着就能赚大钱”?ChatGPT火了,有人早就动起坏脑筋》等。 最近我也使用 ChatGPT 写技术文章了,比如:《…...

单机分布式一体化是什么?真的是数据库的未来吗,OceanBase或将开启新的里程碑

一. 数据 我们先说说数据这个东西&#xff0c;这段时间的ChatGPT在全世界的爆火说明了一件事&#xff0c;数据是有用的&#xff0c;并且大量的数据如果有一个合适的LLM大规模语言模型训练之后&#xff0c;可以很高程度的完成很多意想不到的事情。 我们大多数的时候的注意力只…...

100天精通Python丨基础知识篇 —— 03、Python基础知识扫盲(第一个Python程序,13个小知识点)

文章目录&#x1f41c; 1、Python 初体验Pycharm 第一个程序交互式编程第一个程序&#x1f41e; 2、Python 引号&#x1f414; 3、Python 注释&#x1f985; 4、Python 保留字符&#x1f42f; 5、Python 行和缩进&#x1f428; 6、Python 空行&#x1f439; 7、Python 输出&…...

springboot逍遥大药房管理系统

084-springboot逍遥大药房管理系统演示录像开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&a…...

ZYNQ中的GPIO与AXI GPIO

GPIO GPIO—一种外设&#xff0c;对器件进行观测和控制MIO—将来自PS外设和静态存储器接口的访问多路复用到PS引脚上处理器控制外设的方法—通过一组寄存器包括状态寄存器和控制寄存器&#xff0c;这些寄存器都是有地址的&#xff0c;通过这些寄存器的读写进行外设的控制sessi…...

接口导入功能

1.接口api export function import(param) { return fetch({ url: XXX.import, method: POST, headers: { Content-Type: multipart/form-data; }, data: param }) } 2.页面vue 和 js逻辑 <el-button :loading"disable&qu…...

网络安全知识点总结 期末总结

1、信息安全从总体上可以分成5个层次&#xff0c;密码技术 是信息安全中研究的关键点。 2、握手协议 用于客户机与服务器建立起安全连接之前交换一系列信息的安全信道。 3、仅设立防火墙系统&#xff0c;而没有 安全策略 &#xff0c;防火墙就形同虚设。 4、应用代理防火墙 …...

linux挂载远程目录

服务端操作 # 1、安装NFS程序 yum -y install nfs* rpcbind,在centos6以前自带的yum源中为portmap。 使用yum安装nfs时会下载依赖&#xff0c;因此只要下载nfs即可&#xff0c;无需再下载rpcbind. # 2、查看是否安装了nfs与rpcbind rpm -qa | grep nfs rpm -qa | grep rpc…...

ChatGPT—初识

ChatGPT初识 由于ChatGPT 注册相关的文章被平台限制了&#xff0c;所以有注册相关的问题可以私聊&#xff0c;或者可以代注册 Chat GPT是一款基于GPT模型的对话型AI模型&#xff0c;能够模拟真实的对话风格和行为方式&#xff0c;让人与AI的交互变得更加自然顺畅。下面将从Chat…...

【ArcGIS Pro二次开发】(18):地理处理工具类【Geoprocessing】补遗

ArcGIS Pro SDK 3.0中的Geoprocessing类是用于执行地理处理工具的核心类。地理处理工具是用于执行空间分析、数据转换、数据管理等任务的工具集&#xff0c;包括常见的空间分析工具、栅格处理工具、矢量处理工具、地图制图工具等。 之前有简单记录了下Geoprocessing工具的用法…...

国产芯片方案——红外测温体温计方案

红外测温体温计采用了热电堆式&#xff0c;利用塞贝克效应&#xff0c;将收集到的红外线光信号转化为电信号&#xff0c;再经过放大等处理&#xff0c;按内部的算法校正后再显示屏幕上输出具体温度值&#xff0c;能快速准确地测量人体体温。红外测温体温计广泛应用于医疗卫生、…...

详解ChatGPT的免费总结插件Glarity

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,科大讯飞比赛第三名,CCF比赛第四名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

uniapp 实现腾讯云IM群文件上传下载功能

UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中&#xff0c;群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS&#xff0c;在uniapp中实现&#xff1a; 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...