网站首页 > 技术文章 正文
Blob对象:
Blob(Binary Large Object)二进制类型的大对象,其名称来源于SQL数据库,表示一个不可变、原始数据的类文件对象;
在JavaScript中,它不一定非得是大量数据,其也可以表示一个小型文本文件的内容;
Blob是不透明的,只能获取它们的大小、MIME类型以及将它们分割成更小的Blob;
构造函数:
Blob(blobParts [, options]):返回一个Blob对象,其内容由参数中给定的数组串联组成;
参数:
blobParts是一个由ArrayBuffer、ArrayBufferView、Blob、String等对象构成的Array,或者其他类似对象的混合体,它将会被放入Blob;其中,Strings会被编码为UTF-8;
options是一个可选的BlobPropertyBag([b?ɡ])字典,它可能会指定如下两个属性:
- type,默认值为 "",它代表了将会被放入到blob中的数组内容的MIME类型;
- endings,默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入,它是以下两个值中的一个:"native",代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者 "transparent",代表会保持blob中保存的结束符不变;
var blob = new Blob();
var blob = new Blob([]);
console.log(blob);
var buffer = new ArrayBuffer(32);
var blob = new Blob([buffer]);
console.log(blob);
var int8 = new Int8Array(10);
var blob = new Blob([int8]);
console.log(blob);
var blob = new Blob(['大师哥王唯']);
console.log(blob); // Blob
var newBlob = new Blob([blob]);
console.log(blob);
var aFileParts = ['<a id="a"><b>Web前端开发</b></a>'];
var blob = new Blob(aFileParts, {type: 'text/html'});
console.log(blob); // Blob
还可以通过其他对象创建Blob对象,如:
var person={
username: "王唯",
sex: true,
age: 18
};
var blob = new Blob([JSON.stringify(person, null, 2)],
{type: 'application/json'});
console.log(blob);
Blob表示的不一定是JavaScript原生格式的数据,也可能是File对象,File接口基于Blob,继承了Blob的功能并将其扩展使其支持用户系统上的文件;
<input type="file" id="myfile">
<script>
window.onload = function(){
var myfile = document.getElementById("myfile");
myfile.onchange = function(event){
var file = event.target.files[0];
console.log(file);
console.log(file instanceof Blob); // true
}
}
</script>
Blob属性:
- size:只读,Blob对象中所包含数据的大小(字节);
- type:只读,一个字符串,表明该Blob对象所包含数据的MIME类型;如果类型未知,则该值为空字符串;
console.log(blob.size); // 36
console.log(blob.type); // text/html
Blob方法:
text():返回一个promise且包含blob所有内容的UTF-8格式的字符串;
var blob = new Blob(['大师哥王唯']);
blob.text().then(value => console.log(value));
arrayBuffer():返回一个promise且包含blob所有内容的二进制格式的ArrayBuffer;
var buffer = new ArrayBuffer(32);
var blob = new Blob([buffer]);
blob.arrayBuffer().then(buffer => console.log(buffer)); // ArrayBuffer(32)
slice([start[, end[, contentType]]]):
返回一个新的Blob对象,包含了源Blob对象中指定范围内的数据,即新的Blob对象是源Blob对象的子集;
参数:
- start:可选,代表Blob的下标,表示第一个会被会被拷贝进新的Blob的字节的起始位置,默认值是0;如果传入的是一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算,如果传入的start的长度大于源Blob的长度,那么返回的将会是一个长度为0并且不包含任何数据的一个Blob对象;
- end:可选,代表Blob的下标,该下标-1的对应的字节将会是被拷贝进新的Blob的最后一个字节;如果传入了一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算;默认值是它的原始长度(size);
- contentType:可选,给新的Blob赋予一个新的文档类型,这将会把它的type属性设为被传入的值;默认值是一个空的字符串;
var blob = new Blob(['大师哥王唯']);
var newBlob = blob.slice(0,9,'text/plain');
console.log(newBlob); // size:9
newBlob.text().then(value => console.log(value));
var buffer = new ArrayBuffer(32);
var blob = new Blob([buffer]);
var newBlob = blob.slice(0,6);
newBlob.arrayBuffer().then(buffer => console.log(buffer)); // ArrayBuffer(6)
slice()方法的第二个参数原本可以接受length,以表示复制到新Blob对象的字节数,现在不支持了;
slice()方法在某些浏览器和版本上带有浏览器引擎前缀:如Firefox12及更早版本的mozSlice()和Safari中的webkitSlice();没有浏览器引擎前缀的老版本slice()方法有不同的语义,并且已过时,现在不用了;
浏览器可以将Blob存储到内存中或者磁盘上,Blob可以表示非常大的数据块(比如视频文件),如果事先不用slice()方法将它们分割成小数据块的话,无法存储在主内存中;
正是因为Blob可以表示非常大的数据块,而且它可能需要磁盘的访问权限,所以使用它们的API是异步的;
stream():
返回一个能读取blob内容的ReadableStream对象;
Blob主要用于大量API需要进行二进制数据交换场景,为这些应用提供了通用、高效的数据交换机制,如图:
通过这些API,可以获取Blob对象,例如:
- message事件从其他窗口或者线程中获取Blob;
- 可以从客户端数据库中获取Blob;
- 可以使用XHR2,从Web中下载Blob;
- 还有File对象,它是Blob的子类;
一旦获取了Blob对象,就可以对其进行很多的操作,如:
- 可以使用postMessage()方法向其他窗口或Worker发送一个Blob;
- 可以将Blob存储在客户端数据库中;
- 可以通过将Blob传递给XHR对象的send()方法,来将该Blob上传到服务端;
- 可以使用URL.createObjectURL()函数获取一个特殊的blob://URL,该URL代表Blob的内容,然后,将其和DOM或者CSS结合使用;
- 可以使用FileReader对象来异步地将一个Blob内容抽取成一个字符串或者ArrayBuffer;
- 可以使用File和FileWriter对象,来实现将一个Blob写入到一个本地文件中;
XMLHttpRequest中的Blob:
在HTML 5中,可以通过XML HttpRequest对象的send方法向服务器端发送Blob对象,因为所有File对象(代表一个文件)都是一个Blob对象,所以同样可以通过发送Blob对象的方式来上传文件,如:
<progress max="100" id="progress"></progress>
<input type="button" value="上传" id="btnUpload">
<script>
// 向服务器端发送Blob对象
// 复制当前页面
function uploadDocument(){
var bb= new Blob([document.documentElement.outerHTML],{type: "text/html"});
var xhr = new XMLHttpRequest();
xhr.open('POST', 'sendblob.php?fileName=' + getFileName());
var progressBar = document.getElementById('progress');
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
progressBar.value = (e.loaded / e.total) *100;
console.log("上传成功");
}
}
console.log(bb);
xhr.send(bb);
}
// 获取当前页面文件的文件名
function getFileName(){
var url = window.location.href;
var pos = url.lastIndexOf("\\");
if (pos==-1) // pos==-1表示为本地文件
pos=url.lastIndexOf("/"); // 本地文件路径分割符为"/"
var fileName=url.substring(pos+1); // 从url中获得文件名
return fileName;
}
var btnUpload = document.getElementById("btnUpload");
btnUpload.addEventListener("click", uploadDocument);
</script>
sendblob.php:
<?php
$str = file_get_contents('php://input');
$fileName = 'copy_'.$_REQUEST['fileName'];
$fp = fopen($fileName, 'w');
fwrite($fp, $str);
fclose($fp);
XHR2中的responseType可以指定为blob,以便于从服务器接收Blob数据,如:
<input type="button" value="获取Blob" id="btnGetBlob">
<script>
// 以Blob的形式获取URL指定的内容,并将其传递给指定的回调函数
function getBlobData(url, callback){
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "blob";
xhr.onload = function(){
callback(xhr.response);
};
xhr.send(null);
}
// 如果要下载的数据量很大,想要显示一条进度条,可以使用onprogress事件
function doBlobHandler(blob){
// console.log(blob); // Blob
var url = URL.createObjectURL(blob);
var img = document.createElement("img");
img.src = url;
document.body.appendChild(img);
}
var btnGetBlob = document.getElementById("btnGetBlob");
btnGetBlob.addEventListener("click", function(){
getBlobData("getBlob.php", doBlobHandler);
});
</script>
getBlob.php:
<?php
// echo "Web前端开发";
header("Content-Type: image/png");
$im = imagecreatefrompng("images/1.png");
imagepng($im);
File API:
通常情况下,File对象是来自用户在一个<input>元素上选择文件后files属性返回的FileList集合(或者说是一个类数组)对象,如:
<input type="file" id="myfile" multiple />
<input type="button" value="获取文件" id="btn">
<script>
var myfile = document.getElementById("myfile");
console.log(myfile.files); // FileList {length: 0}
var btn = document.getElementById("btn");
btn.addEventListener("click", function(event){
console.log(myfile.files); // FileList {length: 3}
});
</script>
FileList:
通常一个FileList对象来自于一个HTML的<input>元素的files属性,它是HTML5新增的集合属性,其中包含了一组File对象,每个File对象对应着用户所选择的文件;如果<input>元素具有multiple属性,用户可以选择多个文件,否则,该FileList只能包含一个文件;
// ...
var file = myfile.files[0];
console.log(file); // File
FileList对象具有length属性,其返回列表中的文件数量;
// ...
for(var i=0, len=myfile.files.length; i<len; i++){
var file = myfile.files[i];
console.log(file);
}
FileList对象还有个item(index)方法,其根据index索引值,返回FileList对象中对应的File对象;如:
// ...
var file = myfile.files[i];
// 或者
var file = myfile.files.item(i);
该FileList对象也有可能来自用户的拖放操作;
File接口:
File继承自Blob,是特殊类型的 Blob,且可以用在任意的Blob类型的context中;如:FileReader、 URL.createObjectURL()、createImageBitmap()及XMLHttpRequest.send()都能处理Blob和File;
File构造函数:File(bits, name[, options]);
参数:
- bits:一个包含ArrayBuffer、ArrayBufferView、Blob,或者DOMString对象的Array,或者任何这些对象的组合;即为UTF-8编码的文件内容;
- name:USVString,表示文件名称,或者文件路径;
- options:可选,一个选项对象,包含文件的可选属性;可用的选项如下:
type:DOMString,表示将要放到文件中的内容的MIME类型,默认值为 "";
lastModified:数值,表示文件最后修改时间的 Unix 时间戳(毫秒),默认值为 Date.now();
var file = new File(["zeronetwork"], "myfiles/demo.txt",
{
type:"text/plain",
lastModified: 1654300000000
});
console.log(file); // File
File对象属性:
- lastModified属性:只读,返回当前File对象所引用文件的最后修改时间,自UNIX时间起始以来的毫秒数;
- lastModifiedDate属性:只读,返回当前File对象所引用文件最后修改时间的Date对象;
- name:只读,返回当前File对象所引用的本地文件名字,但由于安全原因,返回的值并不包含文件路径;
- webkitRelativePath非标准属性:只读,返回File相关的path或URL;
- size:以字节为单位返回文件的大小;
- type属性,只读,返回文件的MIME Type;
console.log(file.name); // example.txt
console.log(file.lastModified); // 1649726357207
// Tue Apr 12 2022 09:19:17 GMT+0800
console.log(file.lastModifiedDate);
console.log(file.size); // 15
console.log(file.type); // text/plain
console.log(file.webkitRelativePath); // ""
对于type属性,浏览器不会实际读取文件的字节流,来判断它的媒体类型;它只基于文件扩展名;而且, type属性仅仅对常见文件类型可靠,如图像、文档、音频和视频;不常见的文件扩展名会返回空字符串;
示例:显示选择文件信息
<div>
<input type="file" id="myFiles" name="myFiles" multiple><br/>
共选择 <span id="fileNum">0</span> 个文件,共 <span id="fileSize">0</span>
</div>
<script>
window.onload = function(){
var myFiles = document.getElementById("myFiles");
myFiles.onchange = function(event){
var nBytes = 0, // size
oFiles = event.target.files, // FileList
nFiles = oFiles.length; // Length
for (var i = 0; i < nFiles; i++) {
nBytes += oFiles[i].size;
}
var sOutput = nBytes + " bytes";
var aMultiples = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
for (var nMultiple = 0, nApprox = nBytes / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) {
sOutput = nApprox.toFixed(3) + " " + aMultiples[nMultiple] + " (" + nBytes + " bytes)";
}
document.getElementById("fileNum").innerHTML = nFiles;
document.getElementById("fileSize").innerHTML = sOutput;
}
}
</script>
File类本身没有定义任何方法,但是它从Blob类继承了slice方法,针对大文件传输的场景,我们可以使用 slice 方法对大文件进行切割,然后分片进行上传;
Blob的其它方法,File对象也可以使用,如:
var file = new File(["zeronetwork"], "myfiles/demo.txt");
file.text().then(value => console.log(value)); // zeronetwork
file.arrayBuffer().then(value => console.log(value)); // ArrayBuffer(11)
示例:设置选择文件样式
<div>
<input type="file" id="myFiles" name="myFiles" multiple style="display: none;">
<input type="button" value="选择文件" id="btnSelect">
<br/>
共选择 <span id="fileNum">0</span> 个文件,共 <span id="fileSize">0</span>
</div>
JavaScript:
// ...
var btnSelect = document.getElementById("btnSelect");
btnSelect.addEventListener("click", function(event){
var myFiles = document.getElementById("myFiles");
if(myFiles){
myFiles.click();
}
})
使用label元素也可以触发一个隐藏的file input元素,此时就不需要使用JavaScript的click()方法来打开文件选择器了;
注意在这种情况下,input元素不能使用 display: none(或 visibility: hidden)隐藏,否则 label将无法通过键盘访问,可以使用其他方式模拟隐藏的效果,如:
<style>
.visually-hidden {
position: absolute !important;
height: 1px; width: 1px;overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
}
input.visually-hidden:focus + label {outline: thin dotted;}
</style>
<input type="file" id="myfile" multiple accept="image/*" class="visually-hidden">
<label for="myfile">选择文件</label>
File对象也可以是来拖放操作生成的DataTransfer对象,如,通过拖拽上传文件:
<?php
if (isset($_FILES['myFile'])) {
move_uploaded_file($_FILES['myFile']['tmp_name'], "uploads/" . iconv("UTF-8", "gbk", $_FILES['myFile']['name']));
echo $_FILES['myFile']['name']." 上传成功";
exit;
}
?>
<html>
<head>
<title>上传文件</title>
</head>
<body>
<div>
<div id="dropzone" style="margin:30px; width:500px; height:300px; border:1px dotted grey;">拖放文件到这里...</div>
</div>
<script>
function sendFile(file) {
var url = "./uploaddemo.php";
var xhr = new XMLHttpRequest();
var formData = new FormData();
xhr.open("POST", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
formData.append('myFile', file);
xhr.send(formData);
}
window.onload = function() {
var dropzone = document.getElementById("dropzone");
dropzone.ondragover = dropzone.ondragenter = function(event) {
// 禁止事件传播和阻止默认事件
event.stopPropagation();
event.preventDefault();
}
dropzone.ondrop = function(event) {
event.stopPropagation();
event.preventDefault();
var filesArray = event.dataTransfer.files;
for (var i=0; i<filesArray.length; i++) {
console.log(filesArray[i].name + "," + filesArray[i].type + "," + filesArray[i].size);
sendFile(filesArray[i]);
}
}
}
</script>
</body>
</html>
分片上传大文件:
upload.html
<input type="file" name="file" id="file">
<button id="upload" onClick="upload()">上传</button>
<script>
var chunkSize = 1 * 1024 * 1024; // 每个文件切片大小定为1MB
var totalChunk;
//发送请求
function upload() {
var file = document.getElementById("file").files[0];
var start = 0;
var end;
var index = 0;
var filesize = file.size;
var filename = file.name;
//计算文件切片总数
totalChunk = Math.ceil(filesize / chunkSize);
console.log(totalChunk);
while(start < filesize) {
end = start + chunkSize;
// 匹配最后一个分片的情况
if(end > filesize) {
end = filesize;
}
var chunk = file.slice(start, end); //切割文件
// console.log(chunk);
// 值形式如:mytxt.txt0、mytxt.txt1...
var sliceIndex= file.name + "__" + index;
var formData = new FormData();
formData.append("file", chunk, sliceIndex);
// 如果是最后一个分片,服务端可以合并文件
if(end == filesize){
formData.append("filename", file.name);
formData.append("totalchunk", totalChunk);
formData.append("done", true);
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "postfile.php");
xhr.onload = function(){
console.log(xhr.response);
if(xhr.response && xhr.response.status == 4){
console.log(xhr.response.filename + "上传成功");
}
}
xhr.responseType = "json";
xhr.send(formData);
start = end;
index++;
}
}
</script>
postfile.php:
<?php
$file = $_FILES['file'];
$tempdir = "uploads/tempdir/";
$tempfilename = $tempdir.$file['name'];
if(move_uploaded_file($file['tmp_name'], $tempfilename)){
// 如果所有分片上传完毕,则合并文件
if(isset($_POST['done'])){
$done = $_POST['done'];
$filename = $_POST['filename'];
$totalchunk = $_POST['totalchunk'];
// 合并
$blob = "";
for($i=0; $i<$totalchunk; $i++){
$blob .= file_get_contents($tempdir.$filename."__".$i);
}
file_put_contents("uploads/".$filename, $blob);
// 删除分块文件
for($i=0; $i<$totalchunk; $i++){
@unlink($tempdir.$filename."__".$i);
}
$result = [
"status" => 4,
"filename" => $filename
];
echo json_encode($result);
}
}
递归上传分片文件:
upfile.html:
<input type="file" name="file" id="file">
<button id="upload" onClick="upload()">上传</button>
<script>
var chunkSize = 1 * 1024 * 1024; // 每个文件切片大小定为1MB
var totalChunk;
//发送请求
function upload() {
var file = document.getElementById("file").files[0];
var filesize = file.size;
//计算文件切片总数
totalChunk = Math.ceil(filesize / chunkSize);
console.log(totalChunk);
breakPointUpload(0, totalChunk, chunkSize, file);
}
// i 第几片
function breakPointUpload(i, totalChunk, chunkSize, file){
var start = i * chunkSize; // 当前上传文件块的起始位置
// 当文件末尾不足一个分片时,取小者
// var end = start + chunkSize;
var end = Math.min(file.size, start + chunkSize);
var chunk= file.slice(start, end);
var formData = new FormData();
var sliceIndex= file.name + "__" + i;
formData.append("file", chunk, sliceIndex);
// 如果是最后一个分片
if(i == totalChunk - 1){
formData.append("filename", file.name);
formData.append("totalchunk", totalChunk);
formData.append("done", true);
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "postfile.php");
xhr.onload = function(){
i++;
// 当分片上传达到总分片个数,跳出递归
if(i < totalChunk){
// 递归
setTimeout(function(){
breakPointUpload(i, totalChunk, chunkSize, file);
},0);
}else{
if(xhr.response && xhr.response.status == 4){
console.log(xhr.response.filename + "上传成功");
}
}
}
xhr.responseType = "json";
xhr.send(formData);
}
</script>
带进度条的上传大文件:
upload.html:
<style>
#progress{
width: 300px; height: 20px; background-color:#f7f7f7; margin-bottom: 20px;
box-shadow:inset 0 1px 2px rgba(0,0,0,0.1); border-radius:4px;
background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);
}
#finish{
background-color: #149bdf;
background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);
background-size:40px 40px;
height: 100%;
}
</style>
<div id="progress">
<div id="finish" style="width: 0%;"></div>
</div>
<div>
<input type="file" name="file" id="upfile">
<input type="button" value="停止" id="stop">
</div>
<script>
var upfile = document.getElementById("upfile");
var stopBtn = document.getElementById('stop');
var upload = new Upload();
upfile.onchange = function(){
upload.addFileAndSend(this);
}
stopBtn.onclick = function(){
this.value = "停止中";
upload.stop();
this.value = "已停止";
}
function Upload(){
var xhr = new XMLHttpRequest();
var LENGTH = 1 * 1024 * 1024;
var start = 0;
var end = start + LENGTH;
var file;
var blob;
var blobNum = 1;
var totalBlob = 0;
var isStop = 0;
var md5filename = '';
//对外方法,传入文件对象
this.addFileAndSend = function(that){
file = that.files[0];
totalBlob = Math.ceil(file.size / LENGTH);
blob = cutFile(file);
sendFile(blob, file);
blobNum += 1;
}
//停止文件上传
this.stop = function(){
xhr.abort();
isStop = 1;
}
// 重新开始
this.restart = function(){
sendFile(blob,file);
is_stop = 0;
}
//文件分片
function cutFile(file){
var fileBlob = file.slice(start, end);
start = end;
end = start + LENGTH;
return fileBlob;
};
//发送文件
function sendFile(blob, file){
var formData = new FormData();
formData.append('file',blob);
formData.append('blobNum',blobNum);
formData.append('totalBlob',totalBlob);
formData.append('fileName',file.name);
// formData.append('md5_file_name',md5filename);
xhr.open('POST','./uploadfile.php',false); // 此处使用同步的
xhr.onreadystatechange = function () {
var per;
var progressObj = document.getElementById('finish');
if(totalBlob == 1){
per = '100%';
}else{
per = Math.min(100, (blobNum / totalBlob) * 100 ) +'%';
}
progressObj.style.width = per;
var t = setTimeout(function(){
if(start < file.size && isStop === 0){
blob = cutFile(file);
sendFile(blob, file);
blobNum++;
console.log(start + ":" + blobNum +"/"+ totalBlob);
}else{
clearTimeout(t);
console.log("上传成功");
}
},1000);
}
xhr.send(formData);
}
}
</script>
uploadfile.php:
<?php
class Upload{
private $filepath = './uploads/tempdir'; //上传目录
private $tmpPath; //PHP文件临时目录
private $blobNum; //第几个文件块
private $totalBlobNum; //文件块总数
private $fileName; //文件名
private $md5FileName;
public function __construct($tmpPath, $blobNum, $totalBlobNum, $fileName, $md5FileName){
$this->tmpPath = $tmpPath;
$this->blobNum = $blobNum;
$this->totalBlobNum = $totalBlobNum;
// $this->fileName = $this->createName($fileName, $md5FileName);
$this->fileName = $fileName;
$this->moveFile();
$this->fileMerge();
}
//判断是否是最后一块,如果是则进行文件合成并且删除文件块
private function fileMerge(){
if($this->blobNum == $this->totalBlobNum){
$blob = '';
for($i=1; $i<= $this->totalBlobNum; $i++){
$blob .= file_get_contents($this->filepath.'/'. $this->fileName.'__'.$i);
}
file_put_contents($this->filepath.'/'. $this->fileName,$blob);
$this->deleteFileBlob();
}
}
//删除文件块
private function deleteFileBlob(){
for($i=1; $i<= $this->totalBlobNum; $i++){
@unlink($this->filepath.'/'. $this->fileName.'__'.$i);
}
}
private function moveFile(){
$this->touchDir();
$filename = $this->filepath.'/'. $this->fileName.'__'.$this->blobNum;
move_uploaded_file($this->tmpPath,$filename);
}
//API返回数据
public function apiReturn(){
if($this->blobNum == $this->totalBlobNum){
if(file_exists($this->filepath.'/'. $this->fileName)){
$data['code'] = 2;
$data['msg'] = 'success';
// $data['file_path'] = 'http://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['DOCUMENT_URI']).str_replace('.','',$this->filepath).'/'. $this->fileName;
$data['file_path'] = 'http://'.$_SERVER['HTTP_HOST'].str_replace('.','',$this->filepath).'/'. $this->fileName;
}
}else{
if(file_exists($this->filepath.'/'. $this->fileName.'__'.$this->blobNum)){
$data['code'] = 1;
$data['msg'] = 'waiting';
$data['file_path'] = '';
}
}
header('Content-type: application/json');
echo json_encode($data);
}
private function touchDir(){
if(!file_exists($this->filepath)){
return mkdir($this->filepath);
}
}
private function createName($fileName, $md5FileName){
return $md5FileName . '.' . pathinfo($fileName)['extension'];
}
}
$upload = new Upload($_FILES['file']['tmp_name'], $_POST['blobNum'], $_POST['totalBlob'], $_POST['fileName'], $_POST['md5_file_name']);
$upload->apiReturn();
File对象是特殊类型的Blob,且可以用在任意的Blob类型的context中,比如:FileReader、URL.createObjectURL()、createImageBitmmmap()及XMLHttpRequest.send()都能处理Blob和File;
FileReader:
FileReader对象允许JavaScript异步读取存储在用户计算机上的文件(或原始数据缓存区)的内容,使用File或Blob对象指定要读取的文件或数据;
FileReader经常被用于Web Worker中;
构造函数:FileReader():返回一个FileReader对象,没有参数;
var reader = new FileReader();
console.log(reader); // FileReader
如:
<input type="file" id="myfile" />
<script>
var myfile = document.getElementById("myfile");
myfile.onchange = function(){
var reader = new FileReader();
reader.onload = function(event){
console.log(event.target.result);
};
reader.readAsText(event.target.files[0]);
}
</script>
FileReader对象属性:
- error属性:只读,表示在读取文件时发生的错误;
- readyState属性:只读,表示FileReader状态,可能的值有:
EMPTY(0):还没有加载任何数据;
LOADING(1):数据正在被加载;
DONE(2):已完成全部的读取请求 - result属性:只读,返回文件的内容,其仅在读取操作完成后才有效,并且,数据的格式取决于使用哪个方法来启动读取操作;
myfile.onchange = function(){
var reader = new FileReader();
reader.onload = function(event){
console.log(reader.error); // null
console.log(reader.readyState); // 2
console.log(event.target.result); // content
};
console.log(reader.readyState); // 0
reader.readAsText(event.target.files[0]);
}
FileReader事件:
- onload:处理load事件,即在读取操作完成时触发;
- onloadstart:在读取操作开始时触发;
- onloadend:在读取操作结束时触发;
- onprogress:在读取Blob时反复触发,大概间隔50ms左右;
- onerror:处理error事件,即在读取操作发生错误时触发;
- onabort:处理abort事件,即在读取操作被中断时触发;
// ...
function handler(event){
console.log(event);
}
reader.onabort = handler;
reader.onerror = handler;
reader.onload = handler;
reader.onloadstart = handler;
reader.onloadend = handler;
reader.onprogress = handler;
如:
<input type="file" id="myfile" multiple />
<div id="output"></div>
<progress id="progress" max="100"></progress>
<script>
var myfile = document.getElementById("myfile");
myfile.onchange = function(evt){
var info = "",
output = document.getElementById("output"),
progress = document.getElementById("progress"),
files = evt.target.files,
type = "default",
reader = new FileReader();
if(/image/.test(files[0].type)){
reader.readAsDataURL(files[0]);
type = "image";
}else{
reader.readAsText(files[0]);
type = "text";
}
reader.onerror = function(){
output.innerHTML = "不能读取文件,错误码是:" + reader.error.code;
};
reader.onprogress = function(event){
if(event.lengthComputable)
progress.value = Math.round(event.loaded / event.total * 100);
}
reader.onload = function(event){
switch(type){
case "image":
var img = document.createElement("img");
img.src = event.target.result;
output.appendChild(img);
break;
case "text":
output.appendChild(document.createTextNode(event.target.result));
break;
default:
output.innerHTML = "其它内容...";
break;
}
};
}
</script>
FileReader方法:
abort():中止读取操作;在返回时,readyState属性为DONE;
readAsText(blob[, encoding]):以纯文本形式读取指定的blob中的内容,一旦完成,result属性中将包含一个字符串以表示所读取的文件内容;
可选的参数encoding表示编码类型,如缺省,则默认为“utf-8”类型;
function readfile(f){
var reader = new FileReader();
reader.readAsText(f);
reader.onload = function(){
console.log(reader.result);
};
reader.onerror = function(e){
console.log("error:" + e);
}
}
var myfile = document.getElementById("myfile");
myfile.onchange = function(){
var file = this.files[0];
readfile(file);
}
读取部分内容:
myfile.onchange = function(evt){
var file = evt.target.files[0];
if(file){
var blob = file.slice(0, 20);
var reader = new FileReader();
reader.onload = function(event){
console.log(event.target.result);
}
reader.readAsText(blob);
}
}
readAsArrayBuffer(blob):开始读取参数blob指定的Blob或File对象中的内容,一旦完成,result属性中保存的将是被读取文件的ArrayBuffer数据对象;
<input type="file" id="myfile">
<script>
window.onload = function(){
var myfile = document.getElementById("myfile");
myfile.onchange = function(event){
var file = event.target.files[0];
var reader = new FileReader();
reader.addEventListener("loadend", function (e) {
console.log(e.target.result); // ArrayBuffer
}, false);
if(file){
reader.readAsArrayBuffer(file);
}
}
}
</script>
获取文件类型:以高位优先读取文件的前4个字节:
// 检测指定的blob的前4个字节
// 读出来的内容就是文件的类型,可以将其设置成blob的属性
function typefile(file){
var slice = file.slice(0,4); // 只读取文件起始部分
var reader = new FileReader();
reader.readAsArrayBuffer(slice);
reader.onload = function(e){
var buffer = reader.result;
var view = new DataView(buffer);
var magic = view.getUint32(0, false); // 高位优先,读取4个字节
console.log(magic); // 2303741511
switch(magic){ // 检测文件类型
case 0x89504E47:
file.verified_type = "image/png";
break;
case 0x47494638:
file.verified_type = "image/gif";
break;
case 0x25504446:
file.verified_type = "application/pdf";
break;
case 0x504b0304:
file.verified_type = "application/zip";
break;
}
console.log(file.name, file.verified_type);
};
}
readAsBinaryString(blob):开始读取指定的Blob中的内容,一旦完成,result属性中将包含所读取文件的原始二进制数据,字符串中每个字符表示一个字节,已被废弃,使用readAsArrayBuffer()代替;
var canvas = document.createElement('canvas');
var width = 200, height = 200;
canvas.width = width;
canvas.height = height;
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
ctx.strokeStyle = '#090';
ctx.beginPath();
ctx.arc(width/2, height/2, width/2 - width/10, 0, Math.PI*2);
ctx.stroke();
canvas.toBlob(function(blob){
var reader = new FileReader();
reader.onloadend = function(ev){
console.log(ev.target.result);
}
reader.readAsBinaryString(blob);
});
readAsDataURL(blob):开始读取指定的Blob中的内容,一旦完成,result属性中将包含一个data: URL格式的base64字符串以表示所读取文件的内容;
<input type="file" id="myfile"><br>
<img src="" height="200" alt="图片预览">
<script>
window.onload = function(){
var myfile = document.getElementById("myfile");
myfile.onchange = function(event){
var preview = document.querySelector('img');
var file = event.target.files[0];
var reader = new FileReader();
reader.addEventListener("load", function (e) {
preview.src = e.target.result;
}, false);
if(file){
reader.readAsDataURL(file);
}
}
}
</script>
data URL由四部分组成:data:[<mediatype>][;base64],<data>
- data::前缀;
- [<mediatype>]:MIME type,代表数据的类型,比如'image/jpeg';如果忽略的话,默认是"text/plain;charset=US-ASCII";
- [;base64]:可选的base64标识
- <data>:数据本身,如果是简单的纯文本,可以直接嵌入文本;如果不是纯文本,上一个参数可以标识为base64,并且嵌入base64编码的二进制数据;
读取多个文件,如:
<input type="file" id="myfile" multiple>
<div id="preview"></div>
<script>
function readAndPreview(file) {
var preview = document.querySelector('#preview');
// 确保 `file.name` 符合我们要求的扩展名
if(/\.(jpe?g|png|gif)$/i.test(file.name)){
var reader = new FileReader();
reader.addEventListener("load", function(){
var image = new Image();
image.height = 100;
image.title = file.name;
image.src = this.result;
preview.appendChild(image);
}, false);
reader.readAsDataURL(file);
}
}
window.onload = function(){
var myfile = document.getElementById("myfile");
myfile.onchange = function(event){
var files = event.target.files;
if (files) {
[].forEach.call(files, readAndPreview);
}
}
}
</script>
例子:显示缩略图
function handleFiles(files){
var dropbox = document.getElementById("dropbox");
for(var i=0,len=files.length; i<len; i++){
var file = files[i];
var imageType = /^image\//;
if(!imageType.test(file.type))
continue;
var img = document.createElement("img");
img.classList.add("img");
img.file = file;
dropbox.appendChild(img);
// 此时,img没有src,即还不是真正的图片
// console.log(img);
var reader = new FileReader();
reader.onload = (function(aImg){
return function(e){
aImg.src = e.target.result;
};
})(img);
reader.readAsDataURL(file);
}
}
对象URL(Blob URL):
对象URL也被称为blob URL,指的是引用保存在File或Blob中数据的URL;使用它就可以不必把文件内容读取到JavaScript中而直接使用文件内容;为此,只要在需要文件内容的地方提供对象URL即可;
对象URL主要使用URL类的createobjectURL()和revokeObjectURL()两个静态方法实现;
createObjectURL(object):返回一个DOMString,包含了一个对象URL,该URL可用于指定源object的内容,如包含一个唯一的blob链接;
参数object用于创建URL的File、Blob对象或者MediaSource对象;
使用此方法,可以创建用于引用任何数据的简单URL字符串,也可以引用一个包括用户本地文件的File对象,如:
var myfile = document.getElementById("myfile");
myfile.onchange = function(event){
var file = event.target.files[0];
var objURL = URL.createObjectURL(file);
// blob:http://127.0.0.1:5500/acc683a4-c890-4399-850b-375ebb714f2c
console.log(objURL);
};
这个对象URL是一个标识File对象的字符串,其以“blob://”开始,紧跟着是一小串文本字符串,该字符串用不透明的唯一标识符来标识Blob,指向一块内存地址;
即使对一个已创建了对象URL的文件再次创建一个对象URL,该URL都不相同,如:
// ...
var objURL1 = URL.createObjectURL(file);
// blob:http://127.0.0.1:5500/51a3ee15-d8de-4c57-908f-751baf363bc6
console.log(objURL1);
blob://URL模式被显式的设计成像一个简化的http://URL那样工作,当请求一个blob://URL的时候,浏览器也会像访问HTTP服务器那样做出响应;如果请求的Blob URL已经失效,浏览器必须返回一个404无法找到的状态码;如果请求的Blob URL来自另外的源,那么浏览器必须返回403禁止访问的状态码;
Blob URL只允许通过GET请求获取,并且一旦获取成功,浏览器必须返回一个HTTP 200 OK的状态码,同时响应的Content-Type头信息为该Blob的type属性值,如:“image/png“;
因为这个字符串是一个URL,所以在DOM中也可以使用,如:
myfile.onchange = function(event){
var file = event.target.files[0];
var objURL = URL.createObjectURL(file);
var img = document.createElement("img");
img.src = objURL;
document.body.appendChild(img);
};
revokeObjectURL(objectURL)方法:
在每次调用createObjectURL()方法时,都会创建一个新的URL对象,当不再需要这些 URL 对象时,每个对象必须通过调用revokeObjectURL()方法来释放;虽然关闭浏览器,会自动释放它们,但是为了获得最佳性能和内存使用状况,应该在安全的时机主动释放掉它们;
参数objectURL是一个DOMString,表示通过调用createObjectURL()方法产生的URL对象,如:
// ...
URL.revokeObjectURL(objURL);
示例:使用对象URL来显示多张图片
<input type="file" id="myfile" multiple accept="image/*" style="display: none;" />
<a href="javascript:void(0)" id="fileSelect">选择文件</a>
<div id="fileList">
<p>没有选择的文件</p>
</div>
<script>
window.onload = function(){
window.URL = window.URL || window.webkitURL;
var fileSelect = document.getElementById("fileSelect"),
myfile = document.getElementById("myfile"),
fileList = document.getElementById("fileList");
fileSelect.addEventListener("click", function(e){
if(myfile)
myfile.click();
e.preventDefault();
});
myfile.onchange = function(event){
var files = event.target.files;
if(!files.length)
fileList.innerHTML = "<p>没有选择任何文件</p>";
else{
fileList.innerHTML = "";
var list = document.createElement("ul");
fileList.appendChild(list);
for(var i=0,len=files.length; i<len; i++){
var li = document.createElement("li");
list.appendChild(li);
var img = document.createElement("img");
img.src = URL.createObjectURL(files[i]);
img.height = 80;
img.onload = function(){
// 当图片加载完成之后对象URL就不再需要了
URL.revokeObjectURL(this.src);
};
li.appendChild(img);
var info = document.createElement("span");
info.innerHTML = files[i].name + ":" + files[i].size + "bytes";
li.appendChild(info);
}
}
}
};
</script>
例子:用对象URL显示PDF,对象URL可以用于image之外的其它东西!它可以用于显示嵌入的PDF文件或任何其它浏览器能显示的资源;如:
<input type="file" id="myfile" accept="application/PDF">
<iframe id="viewer"></iframe>
<script>
window.onload = function(){
var myfile = document.getElementById("myfile");
myfile.onchange = function(event){
var file = event.target.files[0];
if(file){
var objURL = URL.createObjectURL(file);
var iframe = document.getElementById("viewer");
iframe.setAttribute('src', objURL);
URL.revokeObjectURL(objURL);
}
};
}
</script>
使用Blob URL来显示一个拖放的文件,如:
<style>
#droptarget{border:2px solid; width:200px; height:200px;}
#droptarget.active{border:4px solid red}
</style>
<div id="droptarget">拖入文件...</div>
<script>
window.onload = function(){
var droptarget = document.getElementById("droptarget");
droptarget.ondragenter = function(event){
// 如果拖的不是文件,就忽略它
var types = event.dataTransfer.types;
if(!types ||
(types.contains && types.contains("Files")) ||
(types.indexOf && types.indexOf("Files") != -1)){
droptarget.classList.add("active");
return false; // 阻止默认行为
}
}
// 如果移出该区域,则取消高这显示
droptarget.ondragover = function(e){ return false;}
// 当用户放下文件,就获取它们的URL,并显示对应的缩略图
droptarget.ondrop = function(e){
var files = e.dataTransfer.files;
for(var i=0; i<files.length; i++){
var type = files[i].type;
var blobUrl = URL.createObjectURL(files[i]);
// if(type.substring(0,6) != "image/"){ // 不是图片则忽略
// continue;
// }
// 我们多显示几种类型的
switch(type){
case "image/jpeg":
case "image/png":
case "image/gif":
var img = document.createElement("img");
img.src = blobUrl;
img.onload = function(){
this.width = 100;
document.body.appendChild(this);
URL.revokeObjectURL(blobUrl);
}
break;
case "application/pdf":
var iframe = document.createElement("iframe");
iframe.setAttribute('src', blobUrl);
document.body.appendChild(iframe);
URL.revokeObjectURL(blobUrl);
break;
case "text/plain":
var reader = new FileReader();
reader.readAsText(files[i]); // 这个直接读取了
reader.onloadend = function(ev){
var div = document.createElement("div");
div.innerHTML = ev.target.result;
document.body.appendChild(div);
reader = null;
}
break;
}
}
droptarget.classList.remove("active");
return false; // 处理完毕
}
}
</script>
例子:将对象URL用于其它文件类型
<video id="myvideo" controls width="250"></video>
<input type="file" id="myfile" accept="video/*">
<script>
window.onload = function(){
var myfile = document.getElementById("myfile");
myfile.onchange = function(event){
var file = event.target.files[0];
if(file){
var myvideo = document.getElementById("myvideo");
var objURL = URL.createObjectURL(file);
myvideo.addEventListener("ended", function(event){
console.log(event);
URL.revokeObjectURL(objURL);
});
myvideo.src = objURL;
}
}
}
</script>
示例:上传Blob数据:
数据表:
CREATE TABLE `uploads` (
`id` int(11) NOT NULL,
`filename` varchar(100) DEFAULT NULL,
`filetype` varchar(50) DEFAULT NULL,
`filesize` int(11) NOT NULL DEFAULT '0',
`filedata` mediumblob
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
uploadblob.html:
<form id="myform">
<input type="file" name="file" id="file">
<input type="button" value="提交" id="btn">
<input type="button" value="提交一个blob" id="btnBlob">
</form>
<script>
var btn = document.getElementById("btn");
btn.addEventListener("click", function(){
var xhr = new XMLHttpRequest();
xhr.open("POST", "blob.php?action=add");
xhr.onload = function(){
console.log(xhr.responseText);
};
// 因为后端可以直接取到文件的type、size,所以前端可以不用处理
var formData = new FormData(document.forms[0]);
xhr.send(formData);
});
var btnBlob = document.getElementById("btnBlob");
btnBlob.addEventListener("click", function(){
var xhr = new XMLHttpRequest();
xhr.open("POST", "blob.php?action=add");
xhr.onload = function(){
console.log(xhr.responseText);
};
var formData = new FormData();
var data = {
name: "wangwei",
age: 18,
sex: true
};
var blob = new Blob([JSON.stringify(data)]);
formData.append("file", blob, "blob_" + new Date().getTime());
xhr.send(formData);
})
</script>
blob.php:
<?php
require_once("conn.php");
if(isset($_GET['action'])){
$action = $_GET['action'];
if($action == "add"){
$file = $_FILES['file'];
// print_r($file);
// exit();
$filename = $file['name'];
$filetype = $file['type'];
$filesize = $file['size'];
$resource = fopen($file['tmp_name'], "rb");
$filedata = addslashes(fread($resource, $filesize));
fclose($resource);
$sql = "insert into uploads(filename, filetype, filesize, filedata)
values('{$filename}','{$filetype}','{$filesize}','{$filedata}')";
$result = $conn->query($sql);
if($result){
$data = array(
"status" => 0
);
}else{
$data = array(
"status" => 1
);
}
echo json_encode($data);
}
}
示例:使用Blob对象存储下载数据:从互联网上下载的数据可以存储到Blob对象中,特别是在一些需要鉴权的接口中,可以使用Ajax请求,将鉴权信息附在请求里,下载得到blob对象,然后将blob作为url使用;或者在前端直接通过构建Blob对象进行前端文件下载,如:
<br/>
<ul id="filelist"></ul>
<script>
window.onload = function(){
var xhr = new XMLHttpRequest();
xhr.open("GET", "blob.php?action=getall&id=1&key=1234");
xhr.onload = function(){
var filelist = document.getElementById("filelist");
var files = xhr.response;
for(var i=0, len=files.length; i<len; i++){
var file = files[i];
var li = document.createElement("li");
li.innerHTML = file.filename + "(" + file.filesize + ")";
var btn = document.createElement("input");
btn.type = "button";
btn.value = "下载";
btn.dataset['fileid'] = file.id;
btn.dataset['filename'] = file.filename;
btn.onclick = downloadHandle;
li.appendChild(btn);
filelist.appendChild(li);
}
};
xhr.responseType = "json";
xhr.withCredentials = true;
xhr.send(null);
}
function downloadHandle(event){
// console.log(event.target.dataset.fileid);
var fileid = event.target.dataset.fileid;
var xhr = new XMLHttpRequest();
// xhr.open("GET", "images/1.jpg");
xhr.open("GET", "blob.php?action=download&id=" + fileid);
xhr.onload = function(){
// console.log(xhr.response);
var url = URL.createObjectURL(xhr.response);
var a = document.createElement("a");
a.setAttribute('download', event.target.dataset.filename);
a.href = url;
a.click();
};
xhr.responseType = "blob";
xhr.withCredentials = true;
xhr.send(null);
}
</script>
blob.php:
elseif($action == "download"){
$id = intval($_GET['id']);
$sql = "select filetype, filedata from uploads where id={$id}";
$query = $conn->query($sql);
$result = mysqli_fetch_assoc($query);
header("Content-Type: ".$result['filetype']);
echo $result['filedata'];
}
- 上一篇: 最全PS快捷键(ps快捷键总结)
- 下一篇: 300 多行代码搞定微信 8.0 的「炸」「裂」特效!
猜你喜欢
- 2024-11-15 CSS3+JS实现静态圆形进度条(css 圆形进度条)
- 2024-11-15 前端必读榜——如何在React中用SpreadJS导入/导出Excel文件
- 2024-11-15 Svelte教程翻译(六、生命周期)(servelet生命周期阶段)
- 2024-11-15 uniapp(Vue) 实现可粘贴的 个性化验证码 输入框
- 2024-11-15 国产开源,GoLang也可以高效处理Excel的利器了
- 2024-11-15 css精髓:这些布局你都学废了吗?(css布局技术)
- 2024-11-15 Java 中的 AI:使用 Spring Boot 和 LangChain 构建 ChatGPT 克隆
- 2024-11-15 仅用18行JavaScript实现一个倒数计时器
- 2024-11-15 Web上的图片技巧「前端篇」(web网页图片)
- 2024-11-15 55个JS代码让你轻松当大神(完整的js代码)
- 标签列表
-
- content-disposition (47)
- nth-child (56)
- math.pow (44)
- 原型和原型链 (63)
- canvas mdn (36)
- css @media (49)
- promise mdn (39)
- readasdataurl (52)
- if-modified-since (49)
- css ::after (50)
- border-image-slice (40)
- flex mdn (37)
- .join (41)
- function.apply (60)
- input type number (64)
- weakmap (62)
- js arguments (45)
- js delete方法 (61)
- blob type (44)
- math.max.apply (51)
- js (44)
- firefox 3 (47)
- cssbox-sizing (52)
- js删除 (49)
- js for continue (56)
- 最新留言
-