Springboot怎么优雅实现大文件的上传
前言
在软件工程里,在处理“大”的时候一直是一个难点和难点,如并发大、数据量大、文件大,对硬件进行升级可以解决一些问题,但这并不最聪明的办法,而对于老板来说,这也不是成本最小的办法。作为开发人员来说,在面对类似极端的问题时,只可智取,不可硬刚,最大化利用好现有的资源,以更加优雅的办法来满足用户多样化的需求。今天的主题也是一个“大”的问题,就是大文件如何上传和下载?其实在解决这个问题之前,有一个问题是绕不过去的:什么才是大文件?几兆?几十兆?几百兆?这其实是一个极具争议的问题,在不同的业务场景下,对于大文件的“大”的理解和定义肯定是不同的。但这并不是本文的重点,这篇文章的重点是想和大家分享一下大文件如何优雅实现大文件的上传和下载。
文章示例环境配置信息
jdk版本:1.8
开发工具:Intellij iDEA 2020.1
springboot:2.3.9.RELEASE
实现思路
对于各种“大”的问题,一般都是采用是分而治之的理念进行设计,而对于大文件上传来说,从这一理念出发,具体的方法就是大文件分片和分片的合并。什么是大文件分片呢?什么又是合并分片呢?如果分片上传过程中,个别分片上传失败了,需要重新分片上传吗?文件上传本身是一个很简单的需求,当量级达到一定程度时,就变得异常复杂了,需要考虑的问题点很多。
大文件分片:将要上传的大文件,按照一定的规则大小,将整个文件进行切片,即把一个大的数据文件切分成小的数据块,然后再按照一定的策略(串行或并发)进行上传;
合并分片:大文件分片合并是大文件分片的后续,当一个大文件被切分成小的数据块上传到服务器时,需要把这些小的数据块按照原来的顺序再进行合并,还原成原来的大文件;
分片和合并其实很好理解,类似于现实生活中,如果想把一架大飞机藏在洞口比较小的山洞里,要怎么做呢?肯定是先把飞机拆成小的零件,然后运输到山洞里面,再根据飞机的装配图纸把所有的零件再装好。原理就这么简单。
断点续传:大文件分片上传的时候,各个分片的上传有可能会碰到网络故障,而导致一些分片文件上传失败,如果网络恢复后,可以从上传失败的分片开始上传而直接跳过上传成功的分片文件部分,可以节约时间,提高上传效率,这就是断点上传;如下图:一个大文件被切分成了n个分片,分片上传过程中,分片2和分片3因为某种原因上传失败了,再次上传的时候,会直接跳过所有上传成功的分片,直接从失败的分片2和分片3开始上传;
实现原理
1、前端使用百度的WebUploader组件,选中待上传的大文件,然后计算出文件的md5值;
2、WebUploader组件开启分片上传后,选中的待上传文件会会按照配置好的分片规则进行分片,分片文件上传前会计算出分片文件的md5值;
3、webuploader组件api计算出分片的md5值后,会携带文件md5值和分片文件的md5值,调用后台接口检验当前分片是否已经上传过;若上传过,则直接跳过;若未上传过,则开始分片上传;
3、前端往后端传输分片文件的过程可以是并发执行,这里一定注意传递到后台的分片并不是按照分片的顺序来的,后端收到分片文件后,会保存分片文件到硬盘、网盘等存储介质上,同是持久化分片文件md5值、文件md5值;
4、待所有的分片上传成功后,会触发WebUploader的uploadSuccess事件,然后再发起合并分片请求;
5、后端再次检查所有的分片是否上传完整,若上传完整,则开始合并所有分片;
md5消息摘要算法,属Hash算法一类,主要特点是不可逆,相同数据的md5值肯定一样,不同数据的md5值不一样;对于数据文件,不管文件名字是否相同,如果数据文件内容相同,则文件的md值是相同的;
webuploader组件提供了文件的md5值的计算方法,其计算过程是异步的;
代码实现
大文件分片上传的实现原理,实际上比较简单和清晰的,那么落到代码实现上,还有几个问题需要解决:
1、webuploader分片上传怎么开启?
2、文件的md5值如何计算?
3、文件如合分片?
4、检验分片是否上传的业务逻辑是什么?
5、分片上传的的请求在哪里触发?
6、合并文件分片的请求在哪里触发?
1、webuploader分片上传怎么开启?
webuploader的分片上传开启实际上很简单,在创建webuploader对象时,设置chunked为true,即表示开启分片上传;chunkSize可以设置分片大小,即以多大的体积进行分片;chunkRetry可以设置重传次数,有的时候由于网络原因,分片上传的会失败,这里即是失败允许重的次数;threads可以设置允许最大由几个进程发起上传请求;
uploader = WebUploader.create({// swf文件路径swf: 'http://localhost:8080/lib/Uploader.swf',// 分片文件上传接口server: 'http://localhost:8080/file/upload',// 选择文件的按钮。可选。pick: '#picker',fileVal: 'multipartFile',//后端用来接收上传文件的参数名称chunked: true,//开启分片上传chunkSize: 1024 * 1024 * 10,//设置分片大小chunkRetry: 2,//设置重传次数,有的时候由于网络原因,分片上传的会失败,这里即是失败允许重的次数threads: 3//允许同时最大上传进程数
});
2、文件的md5值如何计算?
文件的md5计算可以引用spark-md5.js,据传言是javascript里md5加密计算速度最快的,当然在webuploader.js里也有具体的api可以使用,引入webuploader.js后,调用 WebUploader.Uploader.md5File(...)即可计算文件的md5值,这里需要注意的是md5File(...),有三个参数,分别是file,数据起始索引位置、数据结束索引位置,返回的是一个promise对象,要想拿到具体的值还要再调用then(function(val){}),具体逻辑如下:
1、当添加完文件后,webuploader的fileQueued事件被触发;
2、fileQueued事件触发的业务逻辑主要是计算出文件的的md5值,这里注意是计算出整个文件的md5值,而不是一部分,关键是md5File(...)方法的后两个参数数据起始索引位置、数据结束索引位置;md5的计算过程是异步操作,并且文件越大,计算用时越长;
3、deferred的作用就是监控异步计算文件md5值这个异步操作的执行状态;
4、文件md5值计算完成后,更新状态为已完成,这时 deferred.done()会触发;
5、文件md5值计算完成,更新md5计算标志位为true,这时再点击开始上传按钮时,就会再有弹窗提示:md5计算中...,请稍侯;
webuploader内部有很多种command,其中有一个叫before-send-file,也可以在文件上传前会触发,此时还没有开始分片,可以用来做文件整体的md5计算,但是不建议用这个,因为before-send-file的触发时机是要晚于fileQueued事件的;before-send-file是在点击开始上传按钮执行uploader.upload()后才会触发;而fileQueued事件是在选择文件后,立刻触发,不用等到点击开始上传按钮;
/*** 当有文件被添加进队列后触发* 主要逻辑:1、文件被添加到队列后,开始计算文件的md5值;* 2、md5的计算过程是异步操作,并且文件越大,计算用时越长;* 3、变量md5FlagMap是文件md5值计算的标志位,计算完成后,设置当前文件的md5Flag为true*///md5FlagMap用于存储文件md5计算完成的标志位;多个文件时,分别设置标志位,key是文件名,value是true或false;
var md5FlagMap = new Map();
uploader.on('fileQueued', function (file) {md5FlagMap.set(file.name, false);//文件md5值计算的标志位默认为falsevar deferred = WebUploader.Deferred();//deferred用于监控异步计算文件md5值这个异步操作的执行状态uploader.md5File(file, 0, file.size - 1).then(function (fileMd5) {file.wholeMd5 = fileMd5;file_md5 = fileMd5;deferred.resolve(file.name);//文件md5值计算完成后,更新状态为已完成,这时 deferred.done()会触发})//文件越大,文件的md5值计算用时越长,因此md5的计算搞成异步执行是合理的;如果异步执行比较慢的话,会顺序执行到这里$('#thelist').append('<div id="' + file.id + '" class="item">' +'<h4 class="info">' + file.name + '</h4>' +'<p class="state">开始计算大文件的md5......<br/></p>' +'</div>')//文件的md5计算完成,会触发这里的回调函数,deferred.done(function (name) {md5FlagMap.set(name, true);//更新md5计算标志位为true$('#' + file.id).find('p.state').append('大文件的md5计算完成<br/>');})return deferred.promise();
})
3、文件如何分片?
webuploader对象中配置好相关的开启分片设置参数后,当有文件被选中添加后,webuploader会帮你对文件按配置参数进行分片;在分片文件发送到后台之前,webuploader内部另一个command(before-send)会触发,before-send的触发时机上分片上传之前,可以用作在分片发送到之前,计算出分片文件的md5,调用后台接口做分片是否已经上传的验证:如果分片已经上传成功了,直接跳过,不会再调用分片的上传接口;如果分片未上传,则会把分片的md5值赋给分片block上,用于下次上传时分片是否已经上传的校验;
WebUploader.Uploader.register({"add-file": "addFile","before-send-file": "beforeSendFile","before-send": "beforeSend","after-send-file": "afterSendFile"
}, {addFile: function (file) {console.log('1', file)},beforeSendFile: function (file) {console.log('2', file)},beforeSend: function (block) {console.log(3)var file = block.file;var deferred = WebUploader.Base.Deferred();(new WebUploader.Uploader()).md5File(file, block.start, block.end).then(function (value) {$.ajax({url: 'http://localhost:8080/file/check',//检查当前分片是否已经上传method: 'post',data: {chunkMd5: value, fileMd5: file_md5,chunk:block.chunk},success: function (res) {if (res) {deferred.reject();} else {deferred.resolve(value);}}});})deferred.done(function (value) {console.log('分片md5:', value)block.chunkMd5 = value;})return deferred;},afterSendFile: function (file) {console.log('4', file)}
})
4、检验分片是否上传的业务逻辑是什么?
在webuploader内部的一个command(before-send)已经完成了分片文件的md5计算以及请求后台接口来校验当前分片文件是否已经上传,如果已上传,那么会直接跳过当前分片上传接口的调用,uploadBeforeSend事件也不会再触发(当某个分片文件在发送前触发,主要用来询问是否要添加附带参数,大文件在开起分片上传的前提下此事件可能会触发多次);
如果未上传,则uploadBeforeSend事件会触发,携带一些分片的参数信息发起分片上传请求;
// 分片模式下,当文件的分块在发送前触发
uploader.on('uploadBeforeSend', function (block, data) {var file = block.file;console.log('uploadBeforeSend:', block.chunkMd5)var file = block.file;data.originalFilename = file.originalFilename;data.md5Value = file.wholeMd5;data.start = block.start;data.end = block.end;data.chunk = block.chunk;data.chunks = block.chunks;data.chunkMd5 = block.chunkMd5;
});
在后台检验分片是否上传的逻辑很简单:
1、在分片文件上传接口中,这里使用redis缓存了分片md5和文件md5以及其他相关的一些分片信息,用到hash数据结构,key为文件的md5,hashkey是“chunk_md5_”+分片索引,value就是分片文件的md5值;(这里也可以使用数据库来存储)
2、接口被调用的时候,根据前端传过来的当前分片的索引位置取出分片的md5与前端传过来的分片文件md5进行比较,如果相同,则说明当前分片已经上传成功;如果不相同,则说明未上传过;
@PostMapping("/check")
public boolean check(String fileMd5,String chunk,String chunkMd5) {Object o = redisTemplate.opsForHash().get(fileMd5, "chunk_md5_"+chunk);if (chunkMd5.equals(o)) {return true;}return false;
}
/*** 分片上传接口* @param request* @param multipartFile* @return* @throws IOException*/
@PostMapping("/upload")
public String upload(HttpServletRequest request, MultipartFile multipartFile) throws IOException {log.info("分片上传....");Map<String, String> requestParam = this.doRequestParam(request);String md5Value = requestParam.get("md5Value");//整体文件的md5值String chunkIndex = requestParam.get("chunk");//分片在所有分片文件中索引位置Object chunkLocation = redisTemplate.opsForHash().get(md5Value, "chunk_md5_" + chunkIndex);if (chunkLocation != null) {log.info("分片已上传:"+md5Value+"_"+chunkIndex);return "success";}String end = requestParam.get("end");//当前分片在整个数据文件中的结束位置String chunks = requestParam.get("chunks");//文件总共被分了多少片String fileSize = requestParam.get("size");//文件大小String chunkMd5 = requestParam.get("chunkMd5");//分片文件的md5值String userDir = System.getProperty("user.dir");String chunkFilePath = userDir + File.separator + chunkMd5 + "_" + chunkIndex;File file = new File(chunkFilePath);multipartFile.transferTo(file);Map<String,String> map=new HashMap<>();map.put("chunk_location_"+chunkIndex,chunkFilePath);//分片存储路径map.put( "chunk_end_" + chunkIndex, end);map.put("file_size",fileSize);map.put("file_chunks",chunks);map.put("chunk_md5_"+chunkIndex,chunkMd5);redisTemplate.opsForHash().putAll(md5Value,map);return "success";
}
5、分片上传的的请求在哪里触发?
当选择文件后,就开始计算整体文件的md5值了,未计算完成前,点击开始上传按钮,会直接弹出“md5计算中...,请稍侯”;考虑到多文件上传的情况,这里使用了map对象md5FlagMap来存储每个文件的标志,key是文件名称,value是true或false,表示分片文件md5文件是否计算完成;如果不想用按钮来触发上传,WebUploader有一个参数是auto,默认是false,可以改为true,选中文件后自动开始上传;
//开始上传按钮被点击时触发
$('#ctlBtn').click(function () {console.log('ctlBtn')//md5FlagMap存储有文件md5计算的标志位;// 同时上传多个文件时,上传前要判断一下文件的md5是否计算完成,// 如果有未计算完成的,则继续等待计算结果;//文件上传标志位,如果多个文件有一个没有完成md5计算则不能开始上传;这里在实际业务中可以更换成其他交互样式,酌情优化为哪个文件的md5计算完成,则开始哪个文件的上传;var uploadFloag = true;md5FlagMap.forEach(function (value, key) {if (!value) {uploadFloag = false;alert('md5计算中...,请稍侯')//文件md5计算未完成,会弹出弹窗提示;}})if (uploadFloag) {uploader.upload();//文件md5计算完成后,开始分片上传;}
})
相关文章:
Springboot怎么优雅实现大文件的上传
前言在软件工程里,在处理“大”的时候一直是一个难点和难点,如并发大、数据量大、文件大,对硬件进行升级可以解决一些问题,但这并不最聪明的办法,而对于老板来说,这也不是成本最小的办法。作为开发人员来说…...
2月编程语言排行榜新鲜出炉,谁又摘得桂冠?
近日,TIOBE公布了2023年2月编程语言排行榜,本月各个语言表现如何?谁又摘得桂冠?一起来看看吧! TIOBE 2月Top15编程语言: 详细榜单查看TIOBE官网 https://www.tiobe.com/tiobe-index/ 关注IT行业的小伙伴…...
机器学习中的数学原理——模型评估与交叉验证
惭愧惭愧!机器学习中的数学原理这个专栏已经很久没有更新了!前段时间一直在学习深度学习,paddlepaddle,刷题专栏跟新了,这个专栏就被打入冷宫了。这个专栏名为白话机器学习中数学学习笔记,主要是用来分享一…...
JAVA开发(JSP的9大内置对象和4大作用域)
背景: 在springboot横行的javaweb开发中,现在的后端开发工程师基本不需要写前端JSP页面。但是作为web开发工程师,不懂JSP的原理和作用,几乎是不行的。 JSP技术介绍: JSP(全称Java Server Pagesÿ…...
(4)EKF失控保护
文章目录 前言 4.1 什么时候会触发? 4.2 当失控保护触发时,会发生什么?...
数论----质数的求解(C/C++)
CSDN的uu,你们好呀,今天我们要学习的内容是数论哦!这也是算法题中的一类题目吧。记好安全带,准备发车咯!🚀学习数论的意义📢算法导论说:“数论曾经被视为一种虽然优美但却没什么用处…...
【电赛MSP430系列】GPIO、LED、按键、时钟、中断、串口、定时器、PWM、ADC
文章目录MSP430一、GPIO二、点亮LED三、按键控制LED四、更改主时钟五、串口通信六、串口中断七、外部中断八、定时器九、定时器中断十、PWM十一、ADCMSP430 MSP430 是德州仪器(TI)一款性能卓越的超低功耗 16 位单片机,自问世以来,…...
【Linux】进程理解与学习(Ⅱ)
环境:centos7.6,腾讯云服务器Linux文章都放在了专栏:【Linux】欢迎支持订阅🌹相关文章推荐:【Linux】冯.诺依曼体系结构与操作系统【Linux】进程理解与学习(Ⅰ)浅谈Linux下的shell--BASH前言章节…...
vscode 爽到起飞的快捷键
这里写目录标题1. 窗口操作2. 代码编辑3. 批量操作4. 错误处理1. 窗口操作 文件之间切换: CtrlTab 切出一个新的编辑器窗口(最多3个): Ctrl\ 切换左中右3个编辑器窗口的快捷键: Ctrl1 Ctrl2 Ctrl3 2. 代码编辑 代码格式化: ShiftAltF 向上或向下移动一行: Alt…...
vs +qt 打包.cpp和.h为DLL文件
文章目录一 编译成库1 创建一个Qt library 项目2,将已有的文件拷贝到项目目录下3 在项目中添加现有项4,拷贝头文件到需要暴露给外面使用的类的头文件中5 拷贝xxx_EXPORT的宏到需要被暴露的类的名前面6 然后点击编译 就完成了。得到的dll文件在debug里面二…...
echarts有滑块
vue下使用echarts折线图及其横坐标拖拽功能 drawLine() {let that this,lineDate [],dispatchCount [],finishCount [],newCount [];let param {// 参数};axios.post(url, param).then(function(response) {let rs response.data.data;if (rs ! undefined && rs…...
MATLAB绘制ROC曲线
ROC曲线(Receiver Operating Characteristic Curve) 1 简介 ROC曲线是用于评估二元分类模型(如Logistic回归)表现优劣的一种工具,其横轴表示假阳性率(false positive rate,FPR),即实际为负例但…...
ChatGPT前传
文章目录前言GPT概述GPT-1代GPT-1 学习目标和概念介绍GPT-1 训练数据集GPT-1 模型结构和应用细节GPT-1 效果性能和总结GPT-2代GPT-2 学习目标和概念介绍GPT-2 训练数据集GPT-2 模型结构和应用细节GPT-2 性能效果和总结GPT-3代GPT-3 学习目标和概念介绍GPT-3 训练数据集GPT-3 模…...
我的十年编程路 2020年篇
我出生在1990年,2020年到来的时候,我完成了一项成就:奔三。同时,也开启了新的征程:奔四。 2020年的春节是在广州的丈母娘家度过的,春节后大概是初五,或者是初六,我和媳妇就返回天津…...
力扣-SQL【入门】
https://leetcode.cn/study-plan/sql/?progressxhqm4sjh 目录选择595. 大的国家1757. 可回收且低脂的产品584. 寻找用户推荐人183. 从不订购的客户排序 & 修改1873. 计算特殊奖金627. 变更性别196. 删除重复的电子邮箱选择 595. 大的国家 # Write your MySQL query state…...
Vue中组件到底是什么
1.先说结论: Vue中组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。 2.我们使用组件时发生了什么? 比如定义了一个school,然后在页面上使用它 我们只需要写 < school/ > 或< school &…...
不同时间间隔数据对统计结果的影响
目录摘要1. 实测数据来源2. 数据分析方法3 结果分析3.1 波况分析摘要 采用不同的波浪观测方法所获得的波浪数据的时间间隔不一致,其数据的准确性须进行分析。基于大埕湾逐时周年波浪观测数据,截取不同时间间隔的波浪数据,采用统计和相关分析…...
hudi系列-数据写入方式及使用场景
hudi支持多种数据写入方式:insert、bulk_insert、upsert、boostrap,我们可以根据数据本身属性(append-only或upsert)来选择insert和upsert方式,同时也支持对历史数据的高效同步并嫁接到实时流程。 这里的使用技术组合为flink + hudi-0.11 upsert 这是hudi默认的写入方式,…...
C # FileStream文件流
本章讲述:FileStream类的基本功能,以及简单示例; 1、引用命名空间:using System.IO; 2、注意:使用IO操作文件时,要注意流关闭和释放问题! 强力推荐:将创建文件流对象的过程写在usi…...
Go语言中的保留字和运算符详解
前言 🏠个人主页:我是沐风晓月 🧑个人简介:大家好,我是沐风晓月,双一流院校计算机专业,阿里云博客专家 😉😉 💕 座右铭: 先努力成长自己ÿ…...
Linux编译之(1)C语言基础
Linux编译之C语言基础 Author:Once Day Date:2023年3月11日 漫漫长路,才刚刚开始… 1.概述 在Linux下开发多源文件的C代码文件,是一定要了解Makefile的,虽然现在构建工具很多,但学习的一开始࿰…...
CPU平均负载高问题定位分析
一、Linux操作系统CPU平均负载 1.1什么是CPU平均负载 1.2 怎么查看平均负载数值 二、Linux操作系统CPU使用率和平均负载区别 CPU使用率和平均负载区别 三、阿里云Linux操作系统CPU压测环境准备 3.1 核心命令应用场景 3.2 模拟生产环境出现的多种问题环境准备 分析工具安…...
Python蓝桥杯训练:基本数据结构 [二叉树] 中
Python蓝桥杯训练:基本数据结构 [二叉树] 中 文章目录Python蓝桥杯训练:基本数据结构 [二叉树] 中一、[翻转二叉树](https://leetcode.cn/problems/invert-binary-tree/)二、[对称二叉树](https://leetcode.cn/problems/symmetric-tree/)三、[二叉树的最…...
读取 DTC 信息服务 (0x19) – UDS 协议
总目录链接>> AutoSAR入门和实战系列总目录 0x19读取 DTC 信息服务概述 读取 DTC 信息服务在 UDS 协议中用于从车辆或特定 ECU 或节点读取 DTC。UDS 协议的主要任务之一是故障诊断。每当车辆发生任何故障时,与该故障相对应的诊断故障代码(DTC&a…...
Hive 分区表新增字段 cascade
背景 在以前上线的分区表中新加一个字段,并且要求添加到指定的位置列。 模拟测试 加 cascade 操作 创建测试表 create table if not exists sqltest.table_add_column_test(org_col1 string comment 原始数据1,org_col2 string comment 原始数据2 ) comment 增…...
【Java版oj】day08两种排序方法、最小公倍数
目录 一、两种排序方法 (1)原题再现 (2)问题分析 (3)完整代码 二、最小公倍数 (1)原题再现 (2)问题分析 (3)完整代码 一、两种…...
FinOps,从概念到落地 | UGeek大咖说第一期直播回顾(上)
2023年2月28日,由优维科技联合FinOps产业推进方阵举办了第1期「UGeek大咖说-极致用云共济FinOps」线上直播活动,来自中国信通院及美图公司技术专家共同带来了一场精彩的技术视听盛宴。 直 播 背 景 目前,许多以“上云”为数字化转型路径的企…...
k8s java程序实现kubernetes Controller Operator 使用CRD 学习总结
k8s java程序实现kubernetes Controller & Operator 使用CRD 学习总结 大纲 原理Controller 与 Operator自定义资源定义 CRD ( CustomResourceDefinition)kubernetes-client使用java fabric8io/kubernetes-client操作k8s 原生资源使用java abric8io/kubernetes-clientt操…...
Unity笔记:修改代码执行的默认打开方式
使用 External Tools 偏好设置可设置用于编写脚本、处理图像和进行源代码控制的外部应用程序。 External Script Editor:选择 Unity 应使用哪个应用程序来打开脚本文件。Unity 会自动将正确的参数传递给内置支持的脚本编辑器。Unity 内置支持 Visual Studio Commun…...
Linux IPC:匿名管道 与 命名管道
目录一、管道的理解二、匿名管道三、命名管道四、管道的通信流程五、管道的特性进程间通信方式有多种,本文介绍的是管道,管道分为匿名管道和命名管道。 一、管道的理解 生活中的管道用来传输资源,例如水、石油之类的资源。而进程间通信的管道…...
wordpress 文章 收藏/百度客服人工电话24
1、动机 论文中多次提到使用高斯核函数计算距离,虽然学过高数,但是现在我已经忘完了,于是开始慢慢看起来 2、理解 定义 所谓径向基函数 (Radial Basis Function 简称 RBF), 就是某种沿径向对称的标量函数。 通常定义为空间中任一点x到某一中心xc之间欧氏距离的单调函数 …...
策划网站做营销推广/学校教育培训机构
缓存实现的过程以及淘汰旧页面的机制不同,所以会有不同缓存调度方法,就常见的就是FIFO,LRU,LFU缓存过期策略。 1.FIFO(First In First out):先见先出,淘汰最先近来的页面࿰…...
如何做网站备案/搜索引擎营销的主要方法包括
今天在做 wrap 的测试实验的时候,出现一个很奇怪的现象,就是加密不成功。具体表现为:1.加密后的文件大小为0kb。 2.加密后的文件仍然可视。 具体测试步骤如下: D:\Just4work\someSQLs>wrap inametest_oracle_warp.sqlPL/SQL Wr…...
江西seo网站排名优化/免费浏览网站推广
sqlplus据说是不区分大小写的,但是我做了个实验感觉还是区分大小写啊?1)大写SQL> select count(*) from tab where tname like %BIN%;COUNT(*)----------370Elapsed: 00:00:00.07SQL> 2)小写SQL> select count(*) from tab where tname like %…...
北京网站设计优刻/东莞网络排名优化
操作系统(Operating System, OS)是指控制和管理整个计算机系统的硬件和软件资源,并合理地组织调度计算机的工作和资源的分配,以提供给用户和其他软件方便的接口和环境的程序集合。计算机操作系统是随着计算机研究和应用的发展逐步形成并发展起来的&#…...
如何创建自己网站/交换链接名词解释
当把java项目打包成jar后,如何运行main函数呢? 第一种:指定运行类: 1 java -cp test.jar com.ming.test.Test 第二种:在MANIFEST.MF里配置了Main-Class,可以直接执行jar文件 Main-Class: com.ming.test.Test 然后打包执行以下命令…...