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

【文件上传、秒传、分片上传、断点续传、重传】

文章目录

  • 获取文件对象
  • 文件上传(秒传、分片上传、断点续传、重传)
  • 优化

获取文件对象

input标签的onchange方法接收到的参数就是用户上传的所有文件

<html lang="en"><head><title>文件上传</title><style>#inputFile,#inputDirectory {display: none;}#dragarea{width: 100%;height: 100px;border: 2px dashed #ccc;}.dragenter{background-color: #ccc;}</style></head><body><!-- 1. 如何上传多文件:multiple2. 如何上传文件夹:为了兼顾各浏览器兼容性,需设置三个属性:webkitdirectory mozdirectory odirectory3. 如何实现拖拽上传:input默认是有拖拽性质的,但是由于浏览器兼容性问题,开发一般不使用,一般使用div阻止默认事件以及通过拖拽api实现4. 如何获取选择的所有文件--><div id="dragarea"></div><input id="inputFile" type="file" multiple><!-- 如果不想用input自带的上传文件的样式,可以通过button的click触发input的点击事件来上传文件 --><button id="buttonFile">上传文件</button><input id="inputDirectory" type="file" multiple webkitdirectory mozdirectory odirectory><button id="buttonDirectory">上传文件夹</button><ul class="fileList"></ul><script>const inputFile = document.getElementById("inputFile")const buttonFile = document.getElementById("buttonFile")const inputDirectory = document.getElementById("inputDirectory")const buttonDirectory = document.getElementById("buttonDirectory")const dragarea = document.getElementById("dragarea")const fileList = document.getElementById("fileList")const appendFile = (fileList) => {for(const file in fileList){const li = document.getElementById("li")li.innerText = `${file.name}-${file.name.split(".")[1]}-${file.size}`fileList.appendChild(li)}}const traverseFile = (entry) => {if(entry.isFile){entry.file((file) => {const li = document.getElementById("li")li.innerText = `${file.name}-${file.name.split(".")[1]}-${file.size}`fileList.appendChild(li)})}else if(entry.isDirectory){traverseDirectory(entry)}}const traverseDirectory = (directory) => {const reader = directory.createReader()// 创建读取器读取文件夹reader.readEntries((entries) => {for(const entry of entries) {traverseFile(entry)}})}buttonFile.onclick = () => {inputFile.click()}inputFile.onchange = (e) => {const files = e.target.files// 获得用户上传的所有文件appendFile(files)}inputDirectory.onchange = (e) => {console.log(e.target.files)const files = e.target.files// 获得用户上传的所有文件appendFile(files)}buttonDirectory.onclick = () => {inputDirectory.click()}dragarea.ondragenter = (e) => {e.preventDefault();console.log("拖拽进入区域")dragarea.classList.add("dragenter")}dragarea.ondragover = (e) => {e.preventDefault();console.log("拖拽着悬浮在区域上方")dragarea.classList.add("dragenter")}dragarea.ondragleave = (e) => {e.preventDefault();console.log("拖拽离开")dragarea.classList.remove("dragenter")}// 拖拽放开dragarea.ondrop = (e) => {e.preventDefault();dragarea.classList.remove("dragenter")const items = e.dataTransfer.items// 拖拽进来的所有文件for(const item of items){const entry = item.webkitGetAsEntry()traverseFile(entry)}}</script></body>
</html>

文件上传(秒传、分片上传、断点续传、重传)

秒传:调用后端的接口,将md5值传过去,后端判断如果这个md5值对应的文件是否已经合并,如果已经合并,则返回文件上传成功
分片上传:每片大小chunk_size为1m,假如文件1.5m,那么会被分成2片,使用file.slice截取[0,1),再截取[1,1.5)
断点续传:文件上传前会调用后端的接口,将md5值传过去,后端判断如果这个md5值对应的文件是否已经合并,如果没有合并,会返回这个md5值已经上传的切片的索引,前端重新上传剩余索引的片
并发控制:假如我们把文件切成了100片,如果一下子把这100片全传给后端,会给后端造成并发压力,所以在发送前可以在前端进行并发控制一下,我们将所有的请求都放在队列里,每次从队列里弹出几个请求来发送

明明浏览器可以控制请求并发,为什么前端还要自己控制并发请求?

  1. 避免浏览器并发限制:浏览器对同一域名的并发请求数量是有限制的(通常是 6-8 个,具体取决于浏览器和协议)。如果前端不控制并发请求,可能会导致大量请求堆积,超出浏览器的并发限制,从而阻塞其他重要请求(如关键 API 或资源加载),
  2. 提升用户体验:如果一次性发送过多请求,可能会导致网络带宽被占满,影响页面其他资源的加载(如图片、CSS、JS 等),并且可能会导致部分请求超时或失败,从而浪费网络资源和用户流量。
  3. 错误处理和重试机制:手动控制并发可以更好地实现错误处理和重试机制。
    例如,某个请求失败后,可以立即重试,而不是等待所有请求完成后再处理错误。
  4. 优先级控制:手动控制并发可以实现请求的优先级管理。例如,某些关键请求可以优先发送,而低优先级的请求可以稍后处理。
  5. 兼容性和稳定性:不同浏览器对并发请求的处理方式可能不同,手动控制并发可以确保应用在各种浏览器中表现一致。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>文件上传</title><script src="https://cdn.bootcdn.net/ajax/libs/axios/1.7.2/axios.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/spark-md5/3.0.2/spark-md5.js"></script><style>#inputFile,#inputDirectory {display: none;}#dragarea {width: 100%;height: 100px;border: 2px dashed #ccc;}.dragenter {background-color: #ccc;}</style>
</head><body><div class="dragarea"></div><input class="inputFile" type="file" multiple><!-- 如果不想用input自带的上传文件的样式,可以通过button的click触发input的点击事件来上传文件 --><button class="buttonFile">上传文件</button><input class="inputDirectory" type="file" multiple webkitdirectory mozdirectory odirectory><button class="buttonDirectory">上传文件夹</button><button class="buttonUpload">点击上传</button><ul class="fileListElement"></ul><script>// 文件交互相关const fileList = []const chunk_size = 1 * 1024 * 1024const requestQueue = []const maxRequest = 2// 最大请求数量let currentRequest = 0// 当前请求数const inputFile = document.getElementsByClassName("inputFile")[0]const buttonFile = document.getElementsByClassName("buttonFile")[0]const inputDirectory = document.getElementsByClassName("inputDirectory")[0]const buttonDirectory = document.getElementsByClassName("buttonDirectory")[0]const dragarea = document.getElementsByClassName("dragarea")[0]const fileListElement = document.getElementsByClassName("fileListElement")[0]const buttonUpload = document.getElementsByClassName("buttonUpload")[0]// 将上传的文件展示在按钮下方const showFileList = (files) => {for (const file in files) {const li = document.getElementById("li")li.innerText = `${file.name}-${file.name.split(".")[1]}-${file.size}`fileListElement.appendChild(li)fileList.push(file)}}const traverseFile = (entry) => {// 拖拽进来的如果是文件,直接展示在按钮下方if (entry.isFile) {entry.file((file) => {const li = document.getElementById("li")li.innerText = `${file.name}-${file.name.split(".")[1]}-${file.size}`fileList.appendChild(li)})} else if (entry.isDirectory) {// 拖拽进来的如果是文件夹,读文件夹,获得文件夹里面的文件traverseDirectory(entry)}}const traverseDirectory = (directory) => {const reader = directory.createReader()reader.readEntries((entries) => {for (const entry of entries) {traverseFile(entry)}})}buttonFile.onclick = () => {inputFile.click()}inputFile.onchange = (e) => {const files = e.target.files // 获得用户上传的所有文件showFileList(files)}inputDirectory.onchange = (e) => {console.log(e.target.files)const files = e.target.files // 获得用户上传的所有文件showFileList(files)}buttonDirectory.onclick = () => {inputDirectory.click()}dragarea.ondragenter = (e) => {e.preventDefault();console.log("拖拽进入区域")dragarea.classList.add("dragenter")}dragarea.ondragover = (e) => {e.preventDefault();console.log("拖拽着悬浮在区域上方")dragarea.classList.add("dragenter")}dragarea.ondragleave = (e) => {e.preventDefault();console.log("拖拽离开")dragarea.classList.remove("dragenter")}// 拖拽放开dragarea.ondrop = (e) => {e.preventDefault();dragarea.classList.remove("dragenter")const items = e.dataTransfer.itemsfor (const item of items) {const entry = item.webkitGetAsEntry()traverseFile(entry)}}// 文件上传buttonUpload.onclick = () => {for (const file of fileList) {if (file.size <= chunk_size) {uploadSingleFile(file)} else {uploadLargeFile(file)}}}// 单文件一整个文件上传// 文件上传通过formData传输,因为formData是前后端都认识的格式,file是只有前端才认识的格式(后端不认识)const uploadSingleFile = (file) => {const formData = new FormData()formData.append("file", file) // 通过append往formData身上添加对象,如果formData身上已有file对象,会覆盖try {axios.post("http://127.0.0.1:3001/upload", formData, {headers: {"content-type": "multipart/form-data"}})} catch (error) {throw error}}// 大文件上传const uploadLargeFile = async (file) => {// 创建文件hash。创建整个文件的hash即可,每个片不用创建hash,因为每片是调用后端的方法上传的,返回成功即上传成功const md5 = await createFileMd5(file)// 大文件分片const chunksList = createChunkFile(file)// 创建文件分片对象const chunkListObj = createChunkFileObj(chunksList, file, md5)// 将md5值传给后端接口,判断文件是否在服务器上存在,如果存在,后端返回isExistObj.isExists为true,则秒传成功,// 如果不存在,后端会返回给你此md5值上传了哪些片,已上传的片的索引放在chunkIds中const isExistObj = await juedgeFileExist(file, md5)if (isExistObj && isExistObj.isExists) {alert('文件已秒传成功!')return}// 文件上传await asyncPool(chunkListObj, isExistObj.chunkIds) // chunkIds:后端返回的,已上传的分片的索引// await Promise.all(promises)concatChunkFile(file, md5)// 文件上传完毕,调用后端合并文件的接口}// 创建文件的md5值const createFileMd5 = (file) => {return new Promise((resolve, reject) => {const reader = new FileReader()// reader.readAsArrayBuffer(file)读取完毕后会调用onload,读取失败调用onerror,读取到的内容在e.target.result中reader.onload = (e) => {const md5 = SparkMD5.ArrayBuffer.hash(e.target.result)resolve(md5)}reader.onerror = () => {reject(error)}reader.readAsArrayBuffer(file)})}// 创建文件分片:每片大小chunk_size为1m,假如文件1.5m,那么会被分成2片,使用file.slice截取[0,1),再截取[1,1.5)const createChunkFile = (file) => {let current = 0const chunkList = []while (current < file.size) {chunkList.push(file.slice(current, Math.min(current + chunk_size, file.size)))current += chunk_size}return chunkList}// 创建文件分片对象。将文件的md5、文件名、本片在整个文件中的索引,都传入这个对象,调用后端接口上传时会用到的数据都可以封装进来const createChunkFileObj = (chunkList, file, md5) => {return chunkList.map((chunk, index) => {return {file: chunk,md5,name: file.name,index: index,}})}// 文件分片上传const uploadChunkFile = (chunkListObj, chunkIds) => {return chunkListObj.filter((item,index) => (!chunkIds.includes(index)))// 过滤掉已经上传的切片,让已经上传的切片没有下面那个函数.map((chunk, index) => {return () => {const formData = new FormData()formData.append("file", chunk.file, `${chunk.md5}-${chunk.index}`)formData.append("name", chunk.name)formData.append("timestamp", Date.now().toString()) // 防止走缓存try {axios.post("http://127.0.0.1:3001/upload/large", formData, {headers: {"content-type": "multipart/form-data"}})} catch (error) {return Promise.reject(error)throw error}}})}// 判断文件是否存在const juedgeFileExist = async (file, md5) => {try {const response = await axios.post("http://127.0.0.1:3001/upload/exists", formData, {params: {"name": file.nam,md5,}})return response.data.data} catch (error) {return {}throw error}}// 合并请求const concatChunkFile = (file, md5) => {try {axios.post("http://127.0.0.1:3001/upload/concatFiles", {"name": file.nam,md5,})} catch (error) {throw error}}// 把要发送的函数放在队列里,每次从头部取一个函数调用,这样就可以控制并发数量const asyncPool = (chunkListObj, chunkIds) => {return new Promise((resolve,reject) => {requestQueue.push(...uploadChunkFile(chunkListObj, chunkIds))run(resolve,reject)})}const run = (resolve,reject) => {while(currentRequest < maxRequest && requestQueue.length > 0){const task = requestQueue.shift()currentRequest++task().then().finally(() => {currentRequest--run(resolve,reject)})}if(currentRequest === 0 && requestQueue.length === 0) {resolve()}}</script>
</body></html>

优化

相关文章:

【文件上传、秒传、分片上传、断点续传、重传】

文章目录 获取文件对象文件上传&#xff08;秒传、分片上传、断点续传、重传&#xff09;优化 获取文件对象 input标签的onchange方法接收到的参数就是用户上传的所有文件 <html lang"en"><head><title>文件上传</title><style>#inp…...

LabVIEW与PLC交互

一、写法 写命令立即读出 写命令后立即读出&#xff0c;在同一时间不能有多个地方写入&#xff0c;因此需要在整个写入后读出过程加锁 项目中会存在多个循环并行执行该VI&#xff0c;轮询PLC指令 在锁内耗时&#xff0c;就是TCP读写的实际耗时为5-8ms&#xff0c;在主VI六个…...

树莓派5添加摄像头 在C++下调用opencv

由于树莓派5 os系统升级,正常libcamera创建对象每次失败。 改如下方法成功。 1 创建管道 rpicam-vid -t 0 --codec mjpeg -o udp://127.0.0.1:8554 > /dev/null 2>&1 2 opencv从管道里读取 #include <opencv2/opencv.hpp> #include <iostream>int mai…...

【Redis实战】投票功能

1. 前言 现在就来实践一下如何使用 Redis 来解决实际问题&#xff0c;市面上很多网站都提供了投票功能&#xff0c;比如 Stack OverFlow 以及 Reddit 网站都提供了根据文章的发布时间以及投票数计算出一个评分&#xff0c;然后根据这个评分进行文章的展示顺序。本文就简单演示…...

【开源AI】AI一页一页读PDF

【开源AI】AI一页一页读PDF 可以在这里看 : 让AI 处理 PDF 文件,提取其中的知识点,并生成总结。 只是无法修改,后续若有更新在csdn这里。 【OpenAI】 API 更新: JSON 结构化输出约束机制( JSON Schema) 的一次实战。知识库的JSON Schema形式 每一页都要总结,总结的知识…...

Selenium记录RPA初阶 - 基本输入元件

防止自己遗忘&#xff0c;故作此为记录。 爬取网页基本元件并修改后爬取。 包含元件&#xff1a; elements: dict[str, str] {"username": None,"password": None,"email": None,"website": None,"date": None,"ti…...

第三个Qt开发实例:利用之前已经开发好的LED驱动在Qt生成的界面中控制LED2的亮和灭

前言 上一篇博文 https://blog.csdn.net/wenhao_ir/article/details/145459006 中&#xff0c;我们是直接利用GPIO子系统控制了LED2的亮和灭&#xff0c;这篇博文中我们利用之前写好的LED驱动程序在Qt的生成的界面中控制LED2的亮和灭。 之前已经在下面两篇博文中实现了LED驱动…...

Android studio 创建aar包给Unity使用

1、aar 是什么&#xff1f; 和 Jar有什么区别 aar 和 jar包 都是压缩包&#xff0c;可以使用压缩软件打开 jar包 用于封装 Java 类及其相关资源 aar 文件是专门为 Android 平台设计的 &#xff0c;可以包含Android的专有内容&#xff0c;比如AndroidManifest.xml 文件 &#…...

BurpSuite抓包与HTTP基础

文章目录 前言一、BurpSuite1.BurpSuite简介2.BurpSuite安装教程(1)BurpSuite安装与激活(2)安装 https 证书 3.BurpSuite使用4.BurpSuite资料 二、图解HTTP1.HTTP基础知识2.HTTP客户端请求消息3.HTTP服务端响应消息4.HTTP部分请求方法理解5.HTTPS与HTTP 总结 前言 在网络安全和…...

把DeepSeek接入Word软件,给工作提质增效!

前几天给大家分享了 DeepSeek 的资源包&#xff0c;可能很多人并没有本地部署 DeepSeek 的需求&#xff0c;只想使用它来提高一下工作效率。那今天来分享一下怎么直接在 Word 软件调用 DeepSeek&#xff0c;避免在 Word 软件和网页版 DeepSeek 里来回切换。 ## 前置条件 1、有…...

Linux进阶——web服务器

一、相关名词解释及概念&#xff1a; www&#xff1a;(world wide web)全球信息广播&#xff0c;通常来说的上网就是使用www来查询用户所需的信息。使用http超文本传输协议。 过程&#xff1a;web浏览器向web服务&#xff08;Apache&#xff0c;Microsoft&#xff0c;nginx&…...

QT笔记——多语言翻译

文章目录 1、概要2、多语言切换2.1、结果展示2.2、创建项目2.2、绘制UI2.2、生成“.st”文件2.4、生成“.qm”文件2.5、工程demo 1、概要 借助QT自带的翻译功能&#xff0c;实现实际应用用进行 “多语言切换” 2、多语言切换 2.1、结果展示 多语言切换 2.2、创建项目 1、文件…...

oracle 基础语法复习记录

Oracle SQL基础 因工作需要sql能力&#xff0c;需要重新把sql这块知识重新盘活&#xff0c;特此记录学习过程。 希望有新的发现。加油&#xff01;20250205 学习范围 学习SQL基础语法 掌握SELECT、INSERT、UPDATE、DELETE等基本操作。 熟悉WHERE、GROUP BY、ORDER BY、HAVIN…...

php 接入扣子的 token获取

本身逻辑只是个api&#xff0c;但是官方不提供php的sdk 扎心了老铁&#xff0c;这下php 狗都不用了&#xff0c;主要麻烦的是如何获取access_token&#xff0c;代码如下 protected function get_jwt(): string{$header [alg > RS256,typ > JWT,kid > $this->kid];…...

网络工程师 (22)网络协议

前言 网络协议是计算机网络中进行数据交换而建立的规则、标准或约定的集合&#xff0c;它规定了通信时信息必须采用的格式和这些格式的意义。 一、基本要素 语法&#xff1a;规定信息格式&#xff0c;包括数据及控制信息的格式、编码及信号电平等。这是协议的基础&#xff0c;确…...

【银河麒麟高级服务器操作系统】系统日志Call trace现象分析及处理全流程

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://document.kylinos.cn 服务器环境以及配置 系统环境 物理机/虚拟机/云…...

Milvus 存储设计揭秘:从数据写入到 Segment 管理的全链路解析

作为一款云原生向量数据库&#xff0c;Milvus 的高效查询性能有赖于其独特的存储架构设计。然而&#xff0c;在实际使用过程中&#xff0c;许多社区用户常常会遇到以下问题&#xff1a; 为什么频繁调用 flush 后&#xff0c;查询速度会变慢&#xff1f; 数据删除后&#xff0c;…...

Redis双写一致性(数据库与redis数据一致性)

一 什么是双写一致性&#xff1f; 当修改了数据库&#xff08;MySQL&#xff09;中的数据&#xff0c;也要同时更新缓存&#xff08;redis&#xff09;中的数据&#xff0c;缓存中的数据要和数据库中的数据保持一致 双写一致性&#xff0c;根据业务对时间上的要求&#xff0c;…...

14.PPT:中国注册税务师协会宣传【26】

目录 NO12 NO3/4/5​ NO678​ 【文本框水平/垂直居中】【文本框内容水平/垂直居中】 NO12 坑&#xff1a;注意❗Word文档的PPt素材.docx的标题大纲是混乱的&#xff0c;虽然他设置了&#xff0c;所以我们需要重新设置 设计→主题视图→幻灯片母版→删除版式插入logo NO3/4…...

搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程

参考文章&#xff1a; 安装protoc、protoc-gen-go、protoc-gen-go-grpc-CSDN博客 一、简单介绍 本文开发环境&#xff0c;均为 windows 环境&#xff0c;mac 环境其实也类似 ~ ① 编译proto文件&#xff0c;相关插件 简单介绍&#xff1a; protoc 是编译器&#xff0c;用于将…...

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_write_stderr函数

定义 在ngx_log.h 中 static ngx_inline void ngx_write_stderr(char *text) {(void) ngx_write_fd(ngx_stderr, text, ngx_strlen(text)); } 在开发软件时&#xff0c;尤其是像 Nginx 这样复杂的服务器软件&#xff0c;开发者需要一种方法来输出调试信息或者错误信息。这段代码…...

使用requestAnimationFrame减少浏览器重绘

文章目录 介绍使用使用rAF前使用rAF后 介绍 在屏幕中&#xff0c;浏览器通常都以60FPS&#xff08;1/60 s&#xff09;每帧更新屏幕&#xff0c;但是当前端绑定了一些高频事件&#xff0c;如鼠标移动&#xff0c;屏幕滚动、触摸滑动等时&#xff0c;在一帧的周期内&#xff0c;…...

autMan奥特曼机器人-对接deepseek教程

一、安装插件ChatGPT 符合openai api协议的大模型均可使用此插件&#xff0c;包括chatgpt-4/chatgpt-3.5-turbo&#xff0c;可自定义服务地址和模型&#xff0c;指令&#xff1a;gpt&#xff0c;要求Python3.7以上&#xff0c;使用官方库https://github.com/openai/openai-pyt…...

一个精简完整的全栈电商平台采用现代化技术栈和安全架构

以下是一个精简但完整的全栈电商平台实现,采用现代化技术栈和安全架构: 架构设计 前端:SvelteKit + TypeScript (SSR/SSG) 后端:Go 1.21 + Fiber框架 数据库:MySQL 8.0 + Redis 7.0 安全:Cloudflare WAF + JWT+HTTPOnly+Secure Cookie 部署:Docker Swarm + Traefik (自…...

数据分析:企业数字化转型的金钥匙

引言&#xff1a;数字化浪潮下的数据金矿 在数字化浪潮席卷全球的背景下&#xff0c;有研究表明&#xff0c;只有不到30%的企业能够充分利用手中掌握的数据&#xff0c;这是否让人深思&#xff1f;数据已然成为企业最为宝贵的资产之一。然而&#xff0c;企业是否真正准备好从数…...

MySQL中datetime类型23:59:59变成下一天的00:00:00

1. 现象 公司项目中有一个统计的业务需求&#xff0c;需要记录数据的开始和结束时间点&#xff0c;对应的是MySQL表中的datetime类型。结束时间是当天的23:59:59&#xff0c;但是java程序中是对的时间&#xff0c;但是Mybatis-plus入库了之后查看数据库却变成第二天的00:00:00&…...

玩转Docker | 使用Docker部署httpd服务

玩转Docker | 使用Docker部署httpd服务 前言一、准备工作环境确认检查操作系统准备网站目录和配置文件二、拉取httpd镜像三、运行httpd容器运行容器命令检查容器状态四、验证httpd服务浏览器访问测试错误排查五、容器管理与维护查看容器状态停止和启动容器更新网站内容和配置六…...

Vue 双向数据绑定的原理

Vue 的双向数据绑定是其核心特性之一&#xff0c;它可以让视图与数据保持同步&#xff0c;简化了开发者在 DOM 操作上的工作。Vue 的双向数据绑定通过 响应式系统 和 DOM 事件监听 来实现&#xff0c;当数据发生变化时&#xff0c;视图会自动更新&#xff1b;当视图中的元素&am…...

Spring Web MVC项目的创建及使用

一、什么是Spring Web MVC&#xff1f; Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架&#xff0c;从⼀开始就包含在 Spring 框架中&#xff0c;通常被称为Spring MVC。 1.1 MVC的定义 MVC 是 Model View Controller 的缩写&#xff0c;它是软件工程中的一种软件架构…...

32.日常算法

1.最大子数组和 题目来源 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。子数组是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,…...