Javascript文件上传

这篇具有很好参考价值的文章主要介绍了Javascript文件上传。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是文件上传

文件上传包含两部分,

  • 一部分是选择文件,包含所有相关的界面交互。
  • 一部分是网络传输,通过一个网络请求,将文件的数据携带过去,传递到服务器中,剩下的,在服务器中如何存储,那就与前端无关了。

制作文件上传相关的功能时,一定要先确保文件上传的接口可用,否则之后会遇到无数的麻烦,无论怎么写都是写不通的。

可以使用apifox 或者其他的工具先把接口调通。

简单上传的实现

上传界面的制作

抛开文件上传的接口和功能,我们先在本地模拟一个文件上传功能,把静态界面写好,等接口完成后,将模拟的上传替换为真实的接口即可。

但是,很有可能说这些相关的页面无法实现,那就不是文件上传的问题,而是之前的基础不牢固导致的。跟文件上传没有任何关系。

制作下面的一个页面,分为三个区域:文件选择,上传中,上传完成。
Javascript文件上传,JS高级,javascript,开发语言,ecmascript,前端
Javascript文件上传,JS高级,javascript,开发语言,ecmascript,前端
Javascript文件上传,JS高级,javascript,开发语言,ecmascript,前端

这里的文件上传,我们使用一个定时器来模拟。


  1. 首先选择文件并且读取出文件中的数据
// 绑定点击事件
doms.choose.addEventListener('click', (e) => {
   doms.input.click();
})

// 选择文件后执行
doms.input.addEventListener('change', (e) => {
	// 获取文件
	const file = e.target.files[0];
	
	// 使用文件读取器读取
	const reader = new FileReader();
	
	// 获取文件的DataUrl
    reader.readAsDataURL(file);

	// 监听文件加载完成
	render.onload((e) => {
		// 获取临时的DataUrl, 是文件在内存中的临时地址,直接从内存中读取文件
		const url = e.target.result;
		// 开始上传文件,上传的是File, 而不是DataUrl。
		const cancel = load(file, (e) => {
			console.log("上传进度:" + e);
		}, (e) => {
			console.log("上传完成:" + e);
		})
	})
})

  1. 封装上传函数

上传函数暂时先使用一个定时器模拟,之后只需要将这里替换为真实的上传即可。

function load(file, process, finished) {
	// 模拟上传进度
	let pro = 0;
	const timer = setInterval(() => {
		pro++;
		// 监听文件上传进度
		process(pro);
		if(pro >= 100) {
			const result = "文件上传结果"
			clearInterval(timer);
			// 文件上传完成的回调函数
			finished(result);
		}
	}, 50);

	// 返回取消上传的函数
	return function () {
		clearInterval(timer);
	}
}

其余的都是简单的界面交互和事件绑定,如果写不出来就该反思一下之前学的基础怎么样。

发送请求

发送请求只需要改写一下upload函数,将模拟上传改为真实的上传即可。

function upload(file, process, finished) {
	// 这里使用原生的xhr来发送请求
	const xhr = new XMLHttpRequent();
	xhr.onload = function() {
		const resp = JSON.parse(xhr.responseText);
		finished(resp);
	} 
	xhr.upload.onprogress = e => {
		const percent = Math.floor((e.loaded / e.total) * 100);
		process(percent);
	}
	// 这一段看不懂就需要回去学习ajax相关的只是,不是说用axios等第三方库会写就行的
	xhr.open("POST", config.url);
	const form = new FormData();
	form.append('avatar', file);
	xhr.send(form);
	return function() {
		xhr.abort();
	}
}

拖拽上传

拖拽上传和之前的点击上传只有界面交互上的区别,其余完全相同。
所以这里主要写的就是拖拽的交互逻辑。

H5的input文件输入框本身是支持拖拽的,所以我们只需要将默认的文件选择器放置在选择界面并且设置宽高相同,通过调整透明度使其不显示,就可以实现拖拽上传,并且连原本的点击事件都不需要了。

    input {
      display: block;
      width: 100%;
      height: 100%;
      opacity: 0;
      position: absolute;
      left: 0;
      top: 0;
    }

	// doms.choose.addEventListener('click', (e) => {
    //   doms.input.click();
    // })

但是有些时候,为了兼容一些特殊的浏览器,这些浏览器的input不支持拖拽,那我们就必须为我们自己写的上传框注册拖动事件,让他也可以接收托入的文件。


要完成这个目标,首先就需要让输入框编程一个可拖动目标,也就是可以接收拖拽的物体。
只需要将两个事件的默认行为阻止即可。

// 这个事件会在进入拖拽元素后触发,类似mouseenter
doms.choose.ondragenter = (e) => {
	e.preventDefault();
}

// 只要拖拽物一直在拖拽元素之上,就会一直触发,类似mouseover
doms.choose.ondragover = (e) => {
	e.preventDefault();
}

注册上面的两个事件之后,元素就会变为可拖拽目标,就可以监听ondrop事件,这个事件的触发时机是拖拽物在触发元素上松手时,正好符合拖拽上传的操作逻辑。

doms.choose.ondrop = (e) => {
	e.preventDefault();
	const file = e.dataTransfer.files;
	// 开启拖拽后一些奇奇怪怪的东西也可以被拖拽进来,需要做校验
	if (!e.dataTransfer.types.includes("Files")) {
		alert("仅支持拖拽文件");
		return;
	}
	// 现在学习的是单文件上传
	if(file.length !== 1) {
		return;
	}
	doms.input.files = file; // 这样修改并不会触发input标签的change事件,所以需要提取change事件中的函数手动触发。
	changeFile.call(doms.input);
}

doms.input.addEventListener('change', changeFile)

特殊格式上传

base64格式上传

要上传base64格式就不能再使用FormData来构建表单数据了,而是要使用json格式来上传。
依然只需要改写一下uplaod函数即可,为了区分我们另外写一个uploadBase64函数

function uploadBase64(file, process, finished) {
	const ext = file.name.split('.').pop();
	// 使用之前读取DataUrl的方法,逗号后面的字符就是对用的Base64
	const render = new FileReader();
	render.onload = (e) => {
		const base64 = e.target.result.split(',').pop();
		// 拿到base64后开始发送请求
		xhr.open("POST", config.url);
		// 传输json文本需要的请求头
		xhr.setRequestHeader('content-type', 'application/json');
		// 发送json文本
		shr.send(
			JSON.stringify({
				ext,
				avatar: base64
			})
		)
	}
	// 这里使用原生的xhr来发送请求
	const xhr = new XMLHttpRequent();
	xhr.onload = function() {
		const resp = JSON.parse(xhr.responseText);
		finished(resp);
	} 
	xhr.upload.onprogress = e => {
		const percent = Math.floor((e.loaded / e.total) * 100);
		process(percent);
	}
	return function() {
		xhr.abort();
	}
}

二进制文件上传

二进制格式文件的上传是最常见的,也是最简单的,只需要带一个请求头,并且把数据带过去即可。不需要什么FormData呀,base64呀。

function uploadBinary(file, process, finished) {
	// 这里使用原生的xhr来发送请求
	const xhr = new XMLHttpRequent();
	xhr.onload = function() {
		const resp = JSON.parse(xhr.responseText);
		finished(resp);
	} 
	xhr.upload.onprogress = e => {
		const percent = Math.floor((e.loaded / e.total) * 100);
		process(percent);
	}
	xhr.open("POST", config.url);
	// 传输二进制文件需要的请求头
	xhr.setRequestHeader('content-type', 'application/octet-stream');
	// 自定义请求头,根据文档的需求更改
	xhr.setRequestHeader('x-ext', file.name.split('.').pop());
	// 最后直接把文件发过去就可以
	// 这种方式不只是图片,音频、视频等在二进制格式上都是打平的,一视同仁,任何格式的文件都可以上传。
	xhr.send(file);
	return function() {
		xhr.abort();
	}
}

多文件上传

如果想要一次选中多个文件上传,或者是上传一个文件夹中的所有文件,应该怎么做?

如果是点击上传,那么很简单,只需要在input标签上加上几个属性即可
input最后拿到的是一个数组,数组中的每一项都是一个File对象,通过File就可以拿到任何想要的文件信息。

  • multiple: 允许多选
  • webkitdirectory mozdirectory odirectory: 这个属性是让input只能选择文件夹

因为文件夹选择还在实验阶段,所以需要适配不同内核的浏览器


如果是拖拽上传,就相对比较麻烦,需要区分拖拽进来的是文件还是文件夹。
这个相对比较麻烦,暂时先留个坑

裁剪上传

什么是裁剪上传?就是选择一张图片或者其他文件后,可以截取其中的一部分发送到后端,这就是裁剪上传。

这里有两个重点:

  1. 如何实现本地裁剪预览。
  2. 如何实现文件的部分上传。

这里用一个简单的头像上传为例:


实现本地预览很简单,只需要用前面学到的知识,读取出DataUrl即可

const img = $('img')
const input = $('input');

input.onchange = (e) => {
	const file = e.target.files[0];
	const reader = new FileReader();
	reader.readAsDataUrl(file);
	reader.onload = (e) => {
		// 这里拿到的就是DataUrl;
		const url = e.target.result;
		img.src = url;
	}
}

对于图片的裁剪上传,首先需要用之前学过的知识,用原生三剑客写出一个裁剪框来获取用户的裁剪信息,然后利用Canvas就可以实现裁剪和裁剪的预览。

裁剪完成后,通过CanvastoBlob方法拿到Canvas的像素信息对应的Blob对象,进而通过Blob对象合成出File对象实现上传。

const postBtn = $('button');
const img = $('img');

// 这里模拟一个裁剪结果
const cutInfo = {
	x: 100,          // 图像开始裁剪的位置x
	y: 100,          // 图像开始裁剪的位置y
	cutWidth: 300,   // 裁剪图像的宽度
	cutHeight: 300,  // 裁剪图像的高度
	width: 100,      // 裁剪后真实显示的宽度
	height: 100      // 裁剪后真实显示的高度
}

postBtn.onclick = () => {
	const { x, y, cutWidth, cutHeight, width, height } = cutInfo;
	canvas.width = width;
	canvas.height = height;
	ctx.drawImage(img, x, y, cutWidth, cutHeight, 0, 0, width, height);
	// 这个API可以将canvas中的像素信息转换为Blob,有了Blob就可以很容易得获取到File,因为File是Blob的子类,两者之间的转换很容易,有了File就可以上传文件。
	canvas.toBlob((blob) => {
		// new File时传递的时一个数组,因为一个File可以由多个Blob组成。
		const file = new File([blob], 'avatar.jpg', {
			type: 'image/jpeg'
		})
		// 最后通过网络请求将File上传即可。
		ajax(file);
	}, 'image/jpeg');
}

大文件分片上传

对于一些较大的文件,我们一般需要分成一小段一小段,分成多次ajax请求发送到后端。这是因为大文件一旦在传输中出现问题,因为对文件做出了分片,不再需要对整个文件进行上传。

分片上传最大的难点在于如何对文件进行分片?
如何标记已经上传过的文件?

我们知道通过input标签可以拿到File对象,但其实可以将File对象做为一个数组来处理,将File对象用数组的slice方法进行分割,就可以得到若干的Blob对象,Blob对象也是可以直接上传的,这样就实现的文件的分片上传,后端再去将这些分片合成为一个完成的文件。

input.change = (e) => {
	const file = e.target.files[0];
	const blob1 = file.slice(0, 99);
}

// 可以写一个函数来完成文件的分片,接收文件的分片大小,返回Blob数组。
function createChunks(file, size) {
	const cnt = Math.ceil(file.size / size);
	const result = new Array(cnt).fill(0);
	for(let i = 0; i < cnt; i++) {
		result[i] = file.slice(i * size, (i+1) * size);
	}
	return result;
}

注意这个分片是可以瞬间完成的,因为File对象和Blob对象里都只有文件的基本信息,并没有保存详细数据,所以这只是一个简单的数学运算。


之后就需要解决下一个问题,如何标记上传过的文件,这就需要介绍一个算法:文件哈希
文件哈希是一个文件的唯一标识,它可以将一个文件的字节数据按照一定的算法压缩成一个固定长度的字符串,但是这个字符串是不可逆的md5是一个常用的文件哈希算法,可以使用第三方库spark-md5来完成文件哈希的算法。

// 因为计算哈希需要读取内存数据,一次性读取大文件的全部数据吃不消,所以需要使用增量算法,spark-md5这个库有做相关的处理
function hash(chunks) {
    const spark = new SparkMD5();
	function _read(i) {
		if(i >= chunks.length) {
			return spark.end(); // 读取完成
		}
		const blob = chunks[i];
		const reader = new FileReader();
		reader.onload = e => {
			const bytes = e.target.result; // 获取字节数组。
			spark.append(bytes);  // 添加字节到hash运算中。
			read(i + 1);
		}
		// 读取blob对象的字节信息。
		reader.readAsArrayBuffer(blob);
	}
	_read(0);
}

总结

抛开文件上传的外衣,其实就是界面交互和网络请求。

学了这么多的场景,应该足以应对绝大多数的场景,对于element-ui或者是ant等组件库内提供的文件上传组件也能做到知其然且知其所以然。

也学到了对于图片文件的很多处理方式。学到了File和Blob的转换。

对于File和Blob,其实不只是用于图片文件,任何格式的文件在浏览器中都会被打平为File和Blob,只不过不同的文件需要用到不同的辅助处理。

例如图片文件的处理需要接触Image和Canvas,音频文件的处理需要借助Auduo和AudioContent。

这就文件上传部分所有的笔记了。文章来源地址https://www.toymoban.com/news/detail-728473.html

到了这里,关于Javascript文件上传的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • C#结合JavaScript实现多文件上传

    C#结合JavaScript实现多文件上传

    目录 需求 引入 关键代码 操作界面 ​JavaScript包程序 服务端 ashx 程序 服务端上传后处理程序 小结 在许多应用场景里,多文件上传是一项比较实用的功能。实际应用中,多文件上传可以考虑如下需求: 1、对上传文件的类型、大小有一个基本的控制。 2、上传文件时有一个进

    2024年02月05日
    浏览(9)
  • 用JavaScript实现文件的上传与下载

    一、文件上传 1、普通文件上传 JavaScript 可以使用表单提交来实现文件上传。首先,在 HTML 中创建一个文件输入框:  然后,在 JavaScript 中获取文件输入框的引用,并在其上设置事件监听器,如下所示:   在事件监听器中,可以使用 fileInput.files[0] 属性获取选择的文件,然后

    2024年02月06日
    浏览(39)
  • 掌握 JavaScript:从初学者到高级开发者的完整指南(一)

    掌握 JavaScript:从初学者到高级开发者的完整指南(一)

    html完成了架子,css做了美化,但是网页是死的,我们需要给他注入灵魂,所以我们需要学习JavaScript,这门语言会让我们的页面能够和用户进行交互。 同样,js代码也是书写在html中的,那么html中如何引入js代码呢?主要通过下面的2种引入方式: 第一种方式 :内部脚本,将

    2024年02月07日
    浏览(16)
  • 掌握 JavaScript:从初学者到高级开发者的完整指南(三)

    掌握 JavaScript:从初学者到高级开发者的完整指南(三)

    BOM的全称是Browser Object Model,翻译过来是浏览器对象模型。也就是JavaScript将浏览器的各个组成部分封装成了对象。我们要操作浏览器的部分功能,可以通过操作BOM对象的相关属性或者函数来完成。例如:我们想要将浏览器的地址改为 http://www.baidu.com ,我们就可以通过BOM中提供的

    2024年02月06日
    浏览(16)
  • 【前端灵魂脚本语言JavaScript⑤】——JS中数组的使用

    【前端灵魂脚本语言JavaScript⑤】——JS中数组的使用

    🐚 作者: 阿伟 💂 个人主页: Flyme awei 🐋 希望大家多多支持😘一起进步呀! 💬 文章对你有帮助👉关注✨点赞👍收藏📂 第一种: var 数组名 = new Array(); 创建一个空数组 第二种: var arr2 = new Array(10); 创建一个定长为10的数组 第三种 var arr3 = new Array(a,b,c); 创建时直接指定元素值

    2023年04月08日
    浏览(55)
  • 使用javascript-obfuscator给js文件加密

    使用javascript-obfuscator给js文件加密

    注意:其中selfDefending设置为true时比较消耗系统性能,我设置为true时微信开发者工具直接白屏。 出现如下图蓝色字体即成功: 因为被混淆的代码文件是不可读的,所以需要自己备份原文件,以便后续维护修改。 💕 原创不易,还希望各位大佬支持一下 textcolor{blue}{原创不易

    2024年02月07日
    浏览(10)
  • 开发语言漫谈-JavaScript

           JavaScript、Java名字很相近,但它们没有任何亲缘关系,是由不同公司开发的编程语言。Java由Sun公司(后被Oracle收购)开发,JavaScript最初是由Netscape公司开发的(当年浏览器的霸主)。JavaScript最初的名字是 LiveScript,Netscape将其命名为 JavaScript,无非是蹭 Java流量。当

    2024年04月16日
    浏览(13)
  • xlsx.js: 基于 JavaScript 的 Excel 文件读写库

    如果你需要在浏览器端处理 Excel 文件,那么 xlsx.js 可能是一个不错的选择。这是一个基于 JavaScript 的库,可以方便地读取、创建和修改 Excel 文件。 支持多种文件格式:.xlsx, .xlsm, .xlsb, .ods, .csv 等。 支持数据的读取和写入。 支持多种单元格样式,如字体、颜色、背景色等。

    2024年04月17日
    浏览(8)
  • 使用JavaScript和XLSX.js将数据导出为Excel文件

    导出数据是Web应用程序中常见的功能之一。在许多情况下,我们需要将数据导出为Excel文件,以便用户可以在本地计算机上查看和编辑数据。在本篇博客中,我们将介绍如何使用JavaScript和XLSX.js将数据导出为Excel文件。 XLSX.js是一个JavaScript库,它提供了一组API,可以将数据转换

    2024年04月26日
    浏览(12)
  • javaScript实现客户端直连华为云OBS实现文件上传、断点续传、断网重传

    写在前面:在做这个调研时我遇到的需求是前端直接对接华为云平台实现文件上传功能。上传视频文件通常十几个G、客户工作环境网络较差KB/s,且保证上传是稳定的,支持网络异常断点重试、文件断开支持二次拖入自动重传等。综合考虑使用的华为云的分段上传功能,基于

    2024年02月20日
    浏览(11)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包