1. 通过 浏览器的控件使用$_FILE 和服务器后端进行交互上传
True:
{
"name": "HD.Club-4K-Chimei-inn-20mbps.mp4",
"type": "video\/mp4",
"tmp_name": "D:\\mySoft\\wamp64\\tmp\\phpDD30.tmp",
"error": 0,
"size": 80586177
}
False
{"name":"yunnan.mp4","type":"","tmp_name":"","error":1,"size":0}
$_FILE 错误码:
$_FILES['file']['error']
其值为 0,没有错误发生,文件上传成功。
其值为 1,上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值。
其值为 2,上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值。
其值为 3,文件只有部分被上传。
其值为 4,没有文件被上传。
其值为 6,找不到临时文件夹。PHP 4.3.10 和 PHP 5.0.3 引进。
其值为 7,文件写入失败。PHP 5.1.0 引进。
看报错的话,应该是临时文件夹没有写权限或者不存在
配置:
php.ini
upload_max_filesize = 1024
post_max_size = 3072
这两个参数确定了上传文件的大小
nginx.conf
一个有效的分片传例子:
https://www.cnblogs.com/2017sss/p/6654659.html
在该例子实际使用过程中,由于作者只是一个demo版本,实际使用中会出现乱码的问题,我修改了一下。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
#progress{
width: 300px;
height: 20px;
background-color:#f7f7f7;
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%;
}
form{
margin-top: 50px;
}
</style>
</head>
<body>
<div id="progress">
<div id="finish" style="width: 0%;" progress="0"></div>
</div>
<form action="./upload.php">
<input type="file" name="file" id="file">
<input type="button" value="停止" id="stop">
</form>
<script>
var fileForm = document.getElementById("file");
var stopBtn = document.getElementById('stop');
var upload = new Upload();
fileForm.onchange = function(){
upload.addFileAndSend(this);
}
stopBtn.onclick = function(){
this.value = "停止中";
upload.stop();
this.value = "已停止";
}
function Upload(){
var xhr = new XMLHttpRequest();
var form_data = new FormData();
const LENGTH = 5 * 1024 * 1024; // 分片大小
var start = 0;
var end = start + LENGTH;
var blob;
var blob_num = 1;
var is_stop = 0
//对外方法,传入文件对象
this.addFileAndSend = function(that){
var file = that.files[0];
blob = cutFile(file);
sendFile(blob,file);
blob_num += 1;
}
//停止文件上传
this.stop = function(){
xhr.abort();
is_stop = 1;
}
//切割文件
function cutFile(file){
var file_blob = file.slice(start,end);
start = end;
end = start + LENGTH;
return file_blob;
};
//发送文件
function sendFile(blob,file){
var total_blob_num = Math.ceil(file.size / LENGTH);
// 原来采用了append 方法会导致 后续的提交中表单数据过大导致速度原来越慢
//form_data.append('file',blob);
//form_data.append('blob_num',blob_num);
//form_data.append('total_blob_num',total_blob_num);
//form_data.append('file_name',file.name);
// 采用set方法速度一直很稳定
form_data.set('file',blob);
form_data.set('blob_num',blob_num);
form_data.set('total_blob_num',total_blob_num);
form_data.set('file_name',file.name);
xhr.open('POST','./upload.php',false);
xhr.onreadystatechange = function () {
var progress;
var progressObj = document.getElementById('finish');
if(total_blob_num == 1){
progress = '100%';
}else{
progress = Math.min(100,(blob_num/total_blob_num)* 100 ) +'%';
}
progressObj.style.width = progress;
var t = setTimeout(function(){
if(start < file.size && is_stop === 0){
blob = cutFile(file);
sendFile(blob,file);
blob_num += 1;
}else{
setTimeout(t);
}
},1000);
if(xhr.readyState === 4){
if(xhr.status === 200){
console.log(xhr.responseText);
if(xhr.responseText != ''){
data = JSON.parse(xhr.responseText);
if (!(data === null))
{
if (data.code == 2 && data.msg == 'success') {
alert(data.file_path);
}
}
}
}
}
}
xhr.send(form_data);
}
}
</script>
</body>
</html>
upload.php:
<?php
define('SUNCAM_CITY',true);
require_once( 'define.php');
require_once( 'include/function/global.func.php');
class Upload {
private $filepath = './upload'; //上传目录
private $tmpPath; //PHP文件临时目录
private $blobNum; //第几个文件块
private $totalBlobNum; //文件块总数
private $fileName; //文件名
public function __construct($tmpPath, $blobNum, $totalBlobNum, $fileName) {
$this->tmpPath = $tmpPath;
$this->blobNum = $blobNum;
$this->totalBlobNum = $totalBlobNum;
// echo PATH_SEPARATOR==';'?'windows 服务器':'不是 widnows 服务器';
$this->fileName = PATH_SEPARATOR==';' ? iconv("utf-8","gb2312",$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() {
//write_log('good 51');
if ($this->blobNum == $this->totalBlobNum) {
//write_log('good 53');
if (file_exists($this->filepath . '/' . $this->fileName)) {
$data['code'] = 2;
$data['msg'] = 'success';
$data['file_path'] = 'http://' . $_SERVER['HTTP_HOST'] . str_replace('.', '', $this->filepath) . '/' . $this->fileName;
}
} else {
//write_log('good 60');
if (file_exists($this->filepath . '/' . $this->fileName . '__' . $this->blobNum)) {
$data['code'] = 1;
$data['msg'] = 'waiting for all';
$data['file_path'] = '';
}
}
//header('Content-type: application/json'); // 这样设置在浏览器中并没法正确接受到返回值,但是在fiddler中没问题。
header('Content-type: text/html; charset=utf-8'); // 这样的header头 网页上才能得到返回值
echo json_encode($data);
}
//建立上传文件夹
private function touchDir() {
if (!file_exists($this->filepath)) {
return mkdir($this->filepath);
}
}
}
//实例化并获取系统变量传参
$upload = new Upload($_FILES['file']['tmp_name'], $_POST['blob_num'], $_POST['total_blob_num'], $_POST['file_name']);
//调用方法,返回结果
$upload->apiReturn();
这样修改之后代码就稳定了,上传500m和上传50m 的平均速度一样快。