- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
文件上传三种方案:1. form表单上传,2. iframe,3. FormData,base64上传文件,二进制流上传文件,二进制流下载文件。异步上传,大文件上传--切片:拆分上传请求断点续传显示上传进度和暂停上传**
首先要知道我们上传文件时需要修改form表单的 enctype='multipart/form-data'
产生问题:
form表单提交之后会刷新页面
form表单上传大文件时,很容易遇见服务器超时
<form action="http:localhost:8080/uploadFile" method="POST" enctype="multipart/form-data">
<input type="file" name="myfile">
<input type="submit">
</form>
通过canvas讲图片装成base64,然后在服务端进行解码。
base64会将原本的体积转成4/3的体积,so会增大请求体加,浪费带宽,上传和解析的时间会明显增加。
<input type="file" id='file'>
<canvas id='canvas'></canvas>
<img src="" id='target-img'>
<script>
let canvas = document.getElementById("canvas"),
targetImg = document.getElementById('target-img'),
file = document.getElementById('file'),
context = canvas.getContext('2d')
file.onchange = function() {
let URL = window.URL || window.webkitURL
let dataURL = URL.createObjectURL(this.files[0]) // 创建URL对象
let img = new Image()
img.crossOrigin = "anonymous" // 只有服务器模式打开, 才有效
img.src = dataURL
img.onload = function() {
URL.revokeObjectURL(this.src) // img加载完成后,主动释放URL对象
canvas.width = img.width
canvas.height = img.height
context.drawImage(img, 0, 0, img.width, img.height)
let dataBase64Url = canvas.toDataURL('img/png')
targetImg.src = dataBase64Url
}
}
</script>
除了进行base64编码,还可以在前端直接读取文件内容后以二进制格式上传
关键api:
参考
FileReader:对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。
File:对象可以是来自用户在一个<input>
元素上选择文件后返回的files对象
readAsBinaryString: 方法会读取指定的 Blob 或 File 对象,当读取完成的时候,readyState 会变成DONE(已完成),并触发 loadend (en-US) 事件,同时result 属性将包含所读取文件原始二进制格式
Blob: 前端的一个专门用于支持文件操作的二进制对象
ArrayBuffer:前端的一个通用的二进制缓冲区,类似数组,但在API和特性上却有诸多不同
Buffer:Node.js提供的一个二进制缓冲区,常用来处理I/O操作
Uint8Array:类型数组表示的8位无符号整数数组
二进制上传
文件路径格式转二进制
var reader = new FileReader();//①
reader.readAsBinaryString(file);// 把从input里读取的文件内容,放到fileReader的result字段里
reader.onload = function(){
readBinary(this.result) // 读取result或直接上传
}
// 读取二进制文件
function readBinary(text){
var data = new ArrayBuffer(text.length);//创建一个长度为text.length的二进制缓存区
var ui8a = new Uint8Array(data, 0);
for (var i = 0; i < text.length; i++){
ui8a[i] = (text.charCodeAt(i) & 0xff);
}
console.log(ui8a)
}
二进制下载
在向后端发起请求时,需要在请求头中加上
responseType: 'blob'
这样在返回data中可以得到一个浏览器可以解析的blob数据
const downURL = window.URL.createObjectURL(new Blob([data]));
// data 为获取到的二进制数据
const listNode = document.createElement("a");
// 这里注意 : 非同源a标签的download去命名没有用
listNode.download = '合同公允价错误文件下载.xlsx';
listNode.style.display = "none";
listNode.href = downURL;
低版本浏览器上,xhr请求不支持formdata上传,只能form表单上传。
form表单上传,出现的问题上文已经提到,会本身进行页面跳转,产生原因为target属性导致
target我们或多或少有些了解,a标签也有改属性:
_self:默认值,在相同的窗口中打开响应页面
_blank:在新窗口打开
_parent:在父窗口打开
_top:在最顶层的窗口打开
实现方案
实现异步上传的感觉,自理我们就要用到framename去置顶名字的iframe中打开,也就是<iframe name='formtarget'></iframe>
,<form target='formtarget'>
,这样一来返回的数据会被iframe接收,就不会出现刷新问题,而返回的内容可以通过iframe文本拿到。
**问题:**预览图片只有先传给后台,后台再返回一个线上的地址
<iframe id="iframe1" name="formtarget" style="display: none"></iframe>
<form id="fm1" action="/app04/ajax1/" method="POST" target="formtarget" enctype="multipart/form-data">
<input type="file" name="k3"/>
<input type="submit">
</form>
<script>
file.onchange = function() {
let iframe = document.getElementById('iframe1')
iframe.addEventListener("load", function() {
var content = this.contents().
var data = JSON.parse(content)
})
}
</script>
利用FormData模拟表单数据,通过ajax进行提交,可以更加灵活地发送Ajax请求。可以使用FormData来模拟表单提交。
let files = e.target.files // 获取input的file对象
let formData = new FormData();
formData.append('file', file);
axios.post(url, formData);
在同一个请求中,要上传大量的数据,导致整个过程会比较漫长,且失败后需要重头开始上传。
大文件上传我们需要考虑三个方面:
识别切片来源
保证切片拼接顺序
我们一般采用编码的方式进行上传,获取文件对应的二进制内容。
计算出内容的总大小,根据文件大小切成对应的分片。
上传时标识出当前文件,告诉后端上传到了第几个(可以用时间戳形式)。
不加表示的话后端在追加切片时,无法识别切片顺序
接口异常的情况下无法正确拼接
实现
根据文件名、文件长度等基本信息进行拼接,为了避免多个用户上传相同的文件,可以再额外拼接用户信息如uid等保证唯一性
根据文件的二进制内容计算文件的hash,这样只要文件内容不一样,则标识也会不一样,缺点在于计算量比较大.
将文件拆分成piece大小的分块,然后每次请求只需要上传这一个部分的分块即可
let file = document.querySelector("[name=file]").files[0];
const LENGTH = 1024 * 1024 * 0.1;
let chunks = sliceFile(file, LENGTH); // 首先拆分切片
chunks.forEach((chunk,index) => {
let fd = new FormData();
fd.append("file", chunk);
// 传递context
fd.append("context", file.name + file.length);
// 传递切片索引值
fd.append("chunk", index + 1);
upload(fd)
})
function sliceFile(file, piece = 1024 * 1024 * 5) {
let totalSize = file.size; // 文件总大小
let start = 0; // 每次上传的开始字节
let end = start + piece; // 每次上传的结尾字节
let chunks = []
while (start < totalSize) {
// 根据长度截取每次需要上传的数据
// File对象继承自Blob对象,因此包含slice方法
let blob = file.slice(start, end);
chunks.push(blob)
start = end;
end = start + piece;
}
return chunks
}
请求
/**
* 文件上传
* @param {} params
*/
export function upload (params) {
const data = new FormData();
data.append('file', params.file);
data.append('type', params.type);
return $axios({
method: 'post',
url: "/api/Files/upload",
data: data,
headers: {
'Content-Type': 'multipart/form-data',
}
})
}
我们在上传或者下载文件的时候,如果已经进行了一部分,这时候网络故障、页面关闭的情况下,不需要从头开始操作,而是从指定位置继续进行操作,这种处理方式就是所说的“断点续传”
**断点:**的由来是在下载过程中,将一个下载文件分成了多个部分,同时进行多个部分一起的下载,当某个时间点,任务被暂停了,此时下载暂停的位置就是断点了。
续传:一个任务从暂停到开始时,会从上一次任务暂停处开始(可以每次传输成功后加一个表示为告诉前端传输进度)。
实现思路:
实现方案:
进度:我们可以利用xhr.upload.onprogress = Function方法做进度的监听
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
var percent = Math.floor( e.loaded / e.total * 100);//进度计算
if(percent == 100){
}else{
}
}
};
暂停:如果该请求已被发出,XMLHttpRequest.abort() 方法将终止该请求,实现上传暂停的效果。
继续:和断点继传类似,先获取传输的列表,然后重新发送未上传的切片。
Linux 有许多跨(假设是 2 个)CPU 内核执行的线程和进程。我希望我的单线程 C/C++ 应用程序成为 CPU0 上的唯一线程。我如何“移动”所有其他线程以使用 CPU1? 我知道我可以使用
我有一个类似于下图的数据库表 Table with 2 columns (UserId and value) 我将传递 UserId 和 2 个字符串。例如:userId: 1, key1: h1,
我想在我的新项目中使用 ASP.NET Core,因为我听说它更快。但是,该项目将使用广泛的数据库访问功能,Entity Framework Core 不支持其中一些功能。我想知道,是否可以使用 En
我已经使用 EntityFrameworkCore.SqlServer 2.0 开发了 asp .net core wep api 2.0 应用程序。它是使用数据库优先方法开发的。当尝试使用 dbco
我已经阅读了很多关于这个主题的文章,但我仍然无法处理这个问题。对不起,如果它是重复的,无论如何! 所以基本上,我正在从头开始构建一个 Angular 应用程序,并且我想按照最佳约定来组织我的代码。我有
我对MPI还是陌生的,所以如果这是一个琐碎的问题,请原谅我。我有一个四核CPU。我想运行一个在单个内核上使用两个进程的OpenMPI C++程序。有什么办法吗?如果是这样,那又如何?我提到了this
下面是一个传播异常处理机制的类问题,所需的输出是异常。任何人都可以解释为什么输出是异常,在此先感谢。 Class Question { public void m1() throws Excep
我想打印每个获得 CPU 时间片的进程的 name 和 pid。可能吗? 最佳答案 对于单个流程,您可以在以下位置获取此信息: /proc//stat 第14和第15个字段分别代表在用户态和内核态花费
我想知道是否可以识别具有特定 thread-id 的线程使用的物理处理器(核心)? 例如,我有一个多线程应用程序,它有两 (2) 个线程(例如,thread-id = 10 和 thread-id =
我有一个需要身份验证的 Solr 核心。假设我有一个用户,密码为password。当我现在尝试在控制台中创建一个 Solr 核心时 bin\solr create -c test 我收到 HTTP 错
我想为与使用它的项目不同的类库中的第二个和后续数据库创建迁移。有皱纹。我永远不会知道连接字符串,直到用户登录并且我可以从目录数据库 (saas) 中获取它。 对于目录数据库,我使用了来自 this 的
我想为一种可以产生 GHC Core 的简单语言创建一个前端。然后我想获取这个输出并通过正常的 GHC 管道运行它。根据this page , 不能直接通过 ghc 命令实现。我想知道是否有任何方法可
阅读文档,我构建了 2 个使用 BLE 连接 2 个 iDevices 的应用程序。 一个设备是中央设备,另一个是外围设备。 Central在寻找Peripheral,当找到它时,探索它的服务和特性,
在我的网络应用程序中,我对长时间运行的任务进行了操作,我想在后台调用此任务。因此,根据文档 .net core 3.1 Queued background tasks我为此使用这样的代码: publi
Solr 1.4 Enterprise Search Server 建议对核心副本进行大量更新,然后将其换成主核心。我正在按照以下步骤操作: 创建准备核心:http://localhost:8983/
它们是否存在,如果存在,文档和代码在哪里? 最佳答案 它们位于 Git 的 test 目录中。 https://github.com/jquery/jquery/tree/master/test 关于
我有一个 Lisp (SBCL 1.0.40.0.debian) 应用程序 (myfitnessdata),它使用以下代码来处理命令行参数: (:use :common-lisp) (:export
Core是GHC的中间语言。阅读Core可以帮助你更好地了解程序的性能。有人向我索要有关阅读 Core 的文档或教程,但我找不到太多。 有哪些文档可用于阅读 GHC Core? 这是我迄今为止发现的内
我有一个核心 WebJob 部署到 Azure Web 应用程序中。我正在使用WebJobs version 3.0.6 . 我注意到,WebJob 代码不会立即拾取对连接字符串和应用程序设置的更改(
我有一个在内部构造和使用 SqlConnection 类的第三方库。我可以从该类继承,但它有大量重载,到目前为止我一直无法找到合适的重载。我想要的是将参数附加到正在使用的连接字符串。 有没有办法在 .
我是一名优秀的程序员,十分优秀!