파일 업로드는 웹 개발의 일반적인 요구 사항입니다. 파일을 업로드하려면 파일 입력 상자를 사용해야 합니다. 파일 입력 상자에 multiple 속성을 추가하면 한 번에 여러 파일을 선택할 수 있습니다. 브라우저는 자동으로 이 속성을 무시합니다. )
<input multiple type="file">
파일 찾아보기 대화 상자를 열고 파일을 선택하려면 이 입력 상자를 클릭하세요. 일반적으로 하나의 입력 상자로 하나의 파일을 업로드할 수 있습니다. multiple 속성을 지원하는 브라우저를 사용하지 않는 사람들과 호환되도록 이루어졌으며 사용자는 일반적으로 여러 파일을 선택하지 않습니다
(권장 학습: HTML 비디오 튜토리얼 )
기본 업로드
양식에 파일 상자를 입력할 때 양식을 제출할 때 선택한 파일을 함께 제출하여 서버에 업로드할 수 있습니다. 제출된 양식에 파일이 포함되어 있으므로 enctype을 수정해야 합니다. multipart/form -data
<form action="#" enctype="multipart/form-data" method="post"> <input name="file" type="file"> <button type="submit">Upload</button> </form>
에 대한 양식 요소 속성 이 업로드 방법은 업로드된 파일이 큰 경우 업로드가 완료된 후 오랫동안 기다려야 하는 경우가 많습니다. 페이지가 다시 로드되며 작업을 계속하려면 업로드가 완료될 때까지 기다려야 합니다.
조기 탐색 서버는 비동기 업로드를 지원하지 않지만 iframe을 사용하여
<form action="#" enctype="multipart/form-data" method="post" target="upload-frame"> <input name="file" type="file"> <button type="submit">Upload</button> </form> <iframe id="upload-frame" name="upload-frame" src="about:blank" style="display: none;"></iframe>
를 연결합니다. 업로드를 위해 양식을 제출하면 페이지가 다시 로드되지 않습니다. 대신 iframe은 다시 로드되지만 iframe은 원래 숨겨집니다.
파일 액세스
File API는 다음을 제공합니다. 입력 상자의 파일 속성을 통해 파일에 액세스할 수 있습니다. 이는 컬렉션인 FileList를 가져옵니다. 파일을 하나만 선택한 경우 컬렉션의 첫 번째 요소는 이 파일입니다
var input = document.querySelector('input[type="file"]') var file = input.files[0] console.log(file.name) // 文件名称 console.log(file.size) // 文件大小 console.log(file.type) // 文件类型
File API는 caniuse
Ajax upload
를 참조할 수 있습니다. File API를 통해 파일 내용에 직접 접근할 수 있고 XMLHttpRequest 객체와 결합되어 파일을 직접 업로드할 수 있으므로 업로드하려면 에 매개변수로 전달합니다. XMLHttpRequest 개체
var xhr = new XMLHttpRequest() xhr.open('POST', '/upload/url', true) xhr.send(file)의 send
메서드는 어떤 이유로든 이와 같이 파일을 직접 전달하는 것을 권장하지 않습니다. 대신 FormData 개체를 사용하여 업로드해야 하는 파일을 패키지하는 것이 좋습니다. 함수를 사용할 때 먼저 새 인스턴스를 만든 다음 인스턴스의 append 메소드를 통해 데이터를 추가하고 업로드해야 하는 파일을 직접 추가
var formData = new FormData() formData.append('file', file, file.name) // 第 3 个参数是文件名称 formData.append('username', 'Mary') // 还可以添加额外的参数
양식 요소를 인스턴스화 매개변수로 직접 사용할 수도 있으므로 전체 양식에 모든 데이터가 포함되어 있습니다
var formData = new FormData(document.querySelector('form'))
데이터가 준비된 후 업로드됩니다. 또한 XMLHttpRequest 개체의 전송 메서드에 매개 변수로 전달됩니다.
var xhr = new XMLHttpRequest() xhr.open('POST', '/upload/url', true) xhr.send(formData)
XMLHttpRequest 개체도 progress 이벤트를 제공합니다. 이 이벤트는 업로드 진행 상황을 알 수 있습니다
var xhr = new XMLHttpRequest() xhr.open('POST', '/upload/url', true) xhr.upload.onprogress = progressHandler // 这个函数接下来定义
업로드 진행 이벤트는 xhr.upload 개체에 의해 트리거되며 loaded(업로드된 바이트 수) 및 total 이 이벤트 객체의 (총 개수)는 이벤트 핸들러 속성에서 업로드 진행률을 계산하는 데 사용됩니다
function progressHandler(e) { var percent = Math.round((e.loaded / e.total) * 100) }
上面的计算会得到一个表示完成百分比的数字,不过这两个值也不一定总会有,保险一点先判断一下事件对象的 lengthComputable 属性
function progressHandler(e) { if (e.lengthComputable) { var percent = Math.round((e.loaded / e.total) * 100) } }
支持 Ajax 上传的浏览器可以参考 caniuse https://caniuse.com/#feat=xhr2
分割上传
使用文件对象的 slice 方法可以分割文件,给该方法传递两个参数,一个起始位置和一个结束位置,这会返回一个新的 Blob 对象,包含原文件从起始位置到结束位置的那一部分(文件 File 对象其实也是 Blob 对象,这可以通过 file instanceof Blob 确定,Blob 是 File 的父类)
var blob = file.slice(0, 1024) // 文件从字节位置 0 到字节位置 1024 那 1KB
将文件分割成几个 Blob 对象分别上传就能实现将大文件分割上传
function upload(file) { let formData = new FormData() formData.append('file', file) let xhr = new XMLHttpRequest() xhr.open('POST', '/upload/url', true) xhr.send(formData) } var blob = file.slice(0, 1024) upload(blob) // 上传第一部分 var blob2 = file.slice(1024, 2048) upload(blob2) // 上传第二部分 // 上传剩余部分
通常用一个循环来处理更方便
var pos = 0 // 起始位置 var size = 1024 // 块的大小 while (pos < file.size) { let blob = file.slice(pos, pos + size) // 结束位置 = 起始位置 + 块大小 upload(blob) pos += size // 下次从结束位置开始继续分割 }
服务器接收到分块文件进行重新组装的代码就不在这里展示了
使用这种方式上传文件会一次性发送多个 HTTP 请求,那么如何处理这种多个请求同时发送的情况呢?方法有很多,可以用 Promise 来处理,让每次上传都返回一个 promise 对象,然后用 Promise.all 方法来合并处理,Promise.all 方法接受一个数组作为参数,因此将每次上传返回的 promise 对象放在一个数组中
var promises = [] while (pos < file.size) { let blob = file.slice(pos, pos + size) promises.push(upload(blob)) // upload 应该返回一个 promise pos += size }
同时改造一下 upload 函数使其返回一个 promise
function upload(file) { return new Promise((resolve, reject) => { let formData = new FormData() formData.append('file', file) let xhr = new XMLHttpRequest() xhr.open('POST', '/upload/url', true) xhr.onload = () => resolve(xhr.responseText) xhr.onerror = () => reject(xhr.statusText) xhr.send(formData) }) }
当一切完成后
Promise.all(promises).then((response) => { console.log('Upload success!') }).catch((err) => { console.log(err) })
支持文件分割的浏览器可以参考 caniuse
判断一下文件对象是否有该方法就能知道浏览器是否支持该方法,对于早期的部分版本浏览器需要加上对应的浏览器厂商前缀
var slice = file.slice || file.webkitSlice || file.mozSlice if (slice) { let blob = slice.call(file, 0, 1024) // call upload(blob) } else { upload(file) // 不支持分割就只能直接上传整个文件了,或者提示文件过大 }
拖拽上传
通过拖拽 API 可以实现拖拽文件上传,默认情况下,拖拽一个文件到浏览器中,浏览器会尝试打开这个文件,要使用拖拽功能需要阻止这个默认行为
document.addEventListener('dragover', function(e) { e.preventDefault() e.stopPropagation() })
任意指定一个元素来作为释放拖拽的区域,给一个元素绑定 drop 事件
var element = document.querySelector('label') element.addEventListener('drop', function(e) { e.preventDefault() e.stopPropagation() // ... })
通过该事件对象的 dataTransfer 属性获取文件,然后上传即可
var file = e.dataTransfer.files[0] upload(file) // upload 函数前面已经定义
选择类型
给文件输入框添加 accept 属性即可指定选择文件的类型,比如要选择 png 格式的图片,则指定其值为 image/png,如果要允许选择所有类型的图片,就是 image/*
<input accept="image/*" type="file">
添加 capture 属性可以调用设备机能,比如 capture="camera" 可以调用相机拍照,不过这并不是一个标准属性,不同设备实现方式也不一样,需要注意
<input accept="image/*" capture="camera" type="file">
经测 iOS 设备添加该属性后只能拍照而不能从相册选择文件了,所以判断一下
if (iOS) { // iOS 用 navigator.userAgent 判断 input.removeAttribute('capture') }
不支持的浏览器会自动忽略这些属性
自定义样式
文件输入框在各个浏览器中呈现的样子都不大相同,而且给 input 定义样式也不是那么方便,如果有需要应用自定义样式,有一个技巧,可以用一个 label 关联到这个文件输入框,当点击这个 label 元素的时候就会触发文件输入框的点击,打开浏览文件的对话框,相当于点击了文件输入框一样的效果
<label for="file-input"></label> <input id="file-input" style="clip: rect(0,0,0,0); position: absolute;" type="file">
这时就可以将原本的文件输入框隐藏了,然后给 label 元素任意地应用样式,毕竟要给 label 元素应用样式比 input 方便得多
本文来自PHP中文网,html教程栏目,欢迎学习
위 내용은 웹 개발 시 파일 업로드의 다양한 구현 방법(코드 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!