文件结构
使用前后端分离的形式,一个前端页面文件,一个php文件,用于接收文件,一个目录用于存放文件。
前端页面
先放代码,如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>图片上传</title>
<script>
//实现预览功能
function preview() {
//获取文件框的第一个文件,因为文件有可能上传多个文件,咱这里是一个文件
var file = document.getElementById("fileload").files[0];
//可以进行一下文件类型的判断
var fileType = file.type.split("/")[0];
if(fileType != "image") {
alert("请上传图片")
return;
}
//图片大小的限制
var fileSize = Math.round(file.size / 1024 / 1024);
if(fileSize >= 2) {
alert("请上传小于少于2M的图片");
return;
}
//获取img对象
var img = document.getElementById("image");
//建一条文件流来读取图片
var reader = new FileReader();
//根据url将文件添加的流中
reader.readAsDataURL(file);
//实现onload接口
reader.onload = function(e) {
//获取文件在流中url
url = reader.result;
//将url赋值给img的src属性
img.src = url;
};
}
//实现取消上传功能
function call() {
//将img的src属性赋值为空串
document.getElementById("image").src = "";
//选择文件框的value属性赋值为空串
document.getElementById("fileload").value = "";
}
function upload(){
var formData = new FormData();
formData.append("upfile", document.getElementById('fileload').files[0]);
var xhr = new XMLHttpRequest();
xhr.open('POST', '/study/recv_file.php');
// 上传完成后的回调函数
xhr.onload = function () {
if (xhr.status === 200) {
console.log('上传成功');
} else {
console.log('上传出错');
}
};
// 获取上传进度
xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
var percent = Math.floor(event.loaded / event.total * 100) ;
// 设置进度显示
document.getElementById("J_upload_progress").value = percent;
document.getElementById("J_upload_progress").innerText = percent + "%";
}
};
xhr.send(formData);
}
</script>
</head>
<body>
<input id="fileload" type="file" onchange="preview();" />
<input id="upload_btn" type="button" value="upload" onclick="upload();"/>
<!--建一个文件选择框-->
<input type="button" value="取消" onclick="call();" />
<progress id="J_upload_progress" value="0" max="100"></progress>
<h2>预览</h2>
<div style="width: 400px;height: 400px;border: 1px solid #303030;">
<!--设置一个框放图片-->
<img id="image" width="100%" height="100%" src="" />
<!--放图片的标签-->
</div>
</body>
</html>
页面设计比较简单,以实现功能为主要目的。效果如下图:
页面功能比较简单,使用的设计方法也很传统,就不做过多介绍了
后端代码
<?php
function file_type($filename)
{
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch ($typeCode)
{
case 7790:
$fileType = 'exe';
break;
case 7784:
$fileType = 'midi';
break;
case 8297:
$fileType = 'rar';
break;
case 8075:
$fileType = 'zip';
break;
case 255216:
$fileType = 'jpg';
break;
case 7173:
$fileType = 'gif';
break;
case 6677:
$fileType = 'bmp';
break;
case 13780:
$fileType = 'png';
break;
default:
$fileType = 'unknown: '.$typeCode;
}
//Fix
if ($strInfo['chars1']=='-1' AND $strInfo['chars2']=='-40' ) return 'jpg';
if ($strInfo['chars1']=='-119' AND $strInfo['chars2']=='80' ) return 'png';
return $fileType;
}
try {
// Undefined | Multiple Files | $_FILES Corruption Attack
// If this request falls under any of them, treat it invalid.
if (
!isset($_FILES['upfile']['error']) ||
is_array($_FILES['upfile']['error'])
) {
var_dump($_FILES);
throw new RuntimeException('Invalid parameters.');
}
// Check $_FILES['upfile']['error'] value.
switch ($_FILES['upfile']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new RuntimeException('No file sent.');
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new RuntimeException('Exceeded filesize limit.');
default:
throw new RuntimeException('Unknown errors.');
}
// You should also check filesize here.
if ($_FILES['upfile']['size'] > 2*1024*1024) {
throw new RuntimeException('Exceeded filesize limit.');
}
// DO NOT TRUST $_FILES['upfile']['mime'] VALUE !!
// Check MIME Type by yourself.
$ext = file_type($_FILES['upfile']['tmp_name']);
if ($ext != 'jpg' && $ext != 'bmp' && $ext != 'png' && $ext != 'gif') {
throw new RuntimeException('Invalid file format.');
}
// You should name it uniquely.
// DO NOT USE $_FILES['upfile']['name'] WITHOUT ANY VALIDATION !!
// On this example, obtain safe unique name from its binary data.
if (!move_uploaded_file(
$_FILES['upfile']['tmp_name'],
sprintf('./uploads/%s.%s',
sha1_file($_FILES['upfile']['tmp_name']),
$ext
)
)) {
throw new RuntimeException('Failed to move uploaded file.');
}
echo 'File is uploaded successfully.';
} catch (RuntimeException $e) {
echo $e->getMessage();
}
?>
其中file_type是从网络查找的一个判断文件类型的算法,因为使用$_FILES变量中的文件类型做判断并不是十分准确,客户端可以造假,file_type这个函数也不是很准备,但对于一般的应用足够了。move_uploaded_file用于把文件从临时目录移动我们的上传目录
结语
今天就写这些吧,下一篇使用三方库(js)来优化代码。
[1]: https://segmentfault.com/a/1190000008791342
[2]: https://blog.csdn.net/lkwan123/article/details/72830955