后端处理上传文件并等比压缩
- 后端等比压缩代码
代码语言:javascript复制
./upload.php
<?php
class UploadImageServer
{
/**
* 默认上传根目录
*
* @Author huaixiu.zhen
* http://litblc.com
* @var string
*/
private $uploadPath = './upload';
/**
* 默认最大宽度,计算缩略比使用
*
* @Author huaixiu.zhen
* http://litblc.com
* @var int
*/
private $maxWidth = 1920;
/**
* 当前上传文件的实际宽度,方便瀑布流的图片预加载
*
* @Author huaixiu.zhen
* http://litblc.com
* @var int
*/
private $imgWidth = 0;
/**
* 当前上传文件的实际高度,方便瀑布流的图片预加载
*
* @Author huaixiu.zhen
* http://litblc.com
* @var int
*/
private $imgHeight = 0;
/**
* 当前上传文件的后缀名(类型)
*
* @Author huaixiu.zhen
* http://litblc.com
* @var string
*/
private $imageExtension = 'jpeg';
/**
* 默认缩略比
*
* @Author huaixiu.zhen
* http://litblc.com
* @var int
*/
private $percent = 1;
/**
* 错误信息
*
* @Author huaixiu.zhen
* http://litblc.com
* @var string
*/
private $errorMsg = '';
/**
* 限制上传文件的最大Size
*
* @Author huaixiu.zhen
* http://litblc.com
* @var int
*/
public $limitSize = 10485760;
/**
* 限制上传文件的后缀类型
*
* @Author huaixiu.zhen
* http://litblc.com
* @var array
*/
public $limitExtension = ['gif','jpg','jpeg','bmp','png', 'PNG', 'JPG', 'JPEG', 'GIF', 'BMP'];
/**
* 限制上传文件的宽度
*
* @Author huaixiu.zhen
* http://litblc.com
* @var int
*/
public $limitWeight = 1000;
/**
* 限制上传文件的高度
*
* @Author huaixiu.zhen
* http://litblc.com
* @var int
*/
public $limitHeight = 500;
/**
* 上传操作
*
* @Author huaixiu.zhen
* http://litblc.com
* @return array
*/
public function uploadImg()
{
if (array_key_exists('post_image', $_FILES)) {
$file = $_FILES['post_image'];
// 检查上传文件格式
if ($this->checkUploadImg($file)) {
// 本地存储路径
$destFileName = $this->uploadPath . '/image/' . $this->code('image-' . time() . '-') . '.' . $this->imageExtension;
// 本地等比例压缩
if ($this->saveImg($file['tmp_name'], $destFileName)) {
/*
// 上传图片到文件系统(七牛、cdn等),可选
try {
} catch (Exception $e) {
return ['code' => '-1', 'message' => '上传文件系统出错!'];
}
// 删除本地的图,仅保留文件系统上的一份,可选
@unlink($destFileName);
*/
return [
'code' => '0',
'data' => [
'img_url' => $destFileName,
'img_width' => $this->imgWidth,
'img_height' => $this->imgHeight
]
];
} else {
return ['code' => '-1', 'message' => $this->errorMsg];
}
} else {
return ['code' => '-1', 'message' => $this->errorMsg];
}
} else {
return ['code' => '-1', 'message' => '上传文件不存在!'];
}
}
/**
* 随机6位数字
*
* @Author huaixiu.zhen
* http://litblc.com
*
* @param string $prefix
*
* @return string
*/
private function code($prefix = '')
{
$code = str_pad(mt_rand(0, 999999), 6, '0', STR_PAD_BOTH);
return $prefix . $code;
}
/**
* 检查文件类型大小和合法性
*
* @Author huaixiu.zhen
* http://litblc.com
*
* @param $file
*
* @return bool
*/
private function checkUploadImg($file)
{
$image = getimagesize($file['tmp_name']);
$imageExtension = ltrim(strrchr($image['mime'], '/'), '/');
//文件上传失败
if ($file['error'] !== 0) {
$this->errorMsg= '文件上传失败!';
return false;
}
// 是否是真实图片文件
if (!$image) {
$this->errorMsg = '非法图像文件';
return false;
}
// 检查文件类型
if (!in_array($imageExtension, $this->limitExtension)) {
$this->errorMsg = '上传文件类型不允许!';
return false;
}
// 检查文件大小
if ($file['size'] > $this->limitSize) {
$this->errorMsg = '上传文件大小超出限制!';
return false;
}
// 检查是否合法上传
if (!is_uploaded_file($file['tmp_name'])) {
$this->errorMsg = '非法上传文件!';
return false;
}
// 图像宽高限制
if ($image[0] < $this->limitWeight || $image[1] < $this->limitHeight) {
$this->errorMsg = '请选择宽度大于1000px、高度大于500px的图片';
return false;
}
$this->imageExtension = $imageExtension;
return true;
}
/**
* 等比压缩并保存
*
* @Author huaixiu.zhen
* http://litblc.com
*
* @param $file
* @param $name
*
* @return mixed
*/
private function saveImg($file, $name)
{
list($width, $height, $type, $attr) = getimagesize($file);
$imageInfo = [
'width' => $width,
'height' => $height,
'type' => image_type_to_extension($type, false),
'attr' => $attr,
];
$this->percent = $this->setPercent($this->maxWidth, $imageInfo['width']);
$create = 'imagecreatefrom' . $imageInfo['type'];
$image = $create($file);
$newWidth = $imageInfo['width'] * $this->percent;
$newHeight = $imageInfo['height'] * $this->percent;
$newThump = imagecreatetruecolor($newWidth, $newHeight);
imagecopyresampled($newThump, $image, 0, 0, 0, 0, $newWidth, $newHeight, $imageInfo['width'], $imageInfo['height']);
// 同类型压缩
$save = "image" . $imageInfo['type'];
// 固定类型jpeg压缩(空间占用小)
// $save = 'imagejpeg';
if ($save($newThump, $name)) {
@chmod($name, 0777);
imagedestroy($newThump);
imagedestroy($image);
// 保存图片的实际高度宽度,方便前端瀑布流展示
$this->imgWidth = $newWidth;
$this->imgHeight = $newHeight;
return true;
} else {
$this->errorMsg= '文件等比压缩失败!';
return false;
}
}
/**
* 设置缩放比例
*
* @Author huaixiu.zhen
* http://litblc.com
*
* @param $maxWidth
* @param $width
*
* @return float|int
*/
private function setPercent($maxWidth, $width)
{
if ($width > $maxWidth) {
$percent = $maxWidth / $width;
} else {
$percent = 1;
}
return $percent;
}
}
代码语言:javascript复制后端调用示例
./index.php
:
<?php
require_once './upload.php';
$uploadImgServer = new UploadImageServer();
$result = $uploadImgServer->uploadImg();
print_r(json_encode($result, JSON_UNESCAPED_UNICODE));
至此后端已经压缩完毕,但是如果上传的图片大多是几M的大图,难免浪费上传带宽,而且会导致速度非常慢,影响用户体验,于是可以使用canvas在上传之前压缩一遍,解决速度慢的问题。
前端使用canvas压缩再上传
- 前端示例代码:
代码语言:javascript复制
./index.html
<html>
<head>
<script src="https://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script>
</head>
<input type="file" class="js-upload-local">
<script>
// 上传本地图片
$('.js-upload-local').change(function(){
var files = $(this)[0].files;
if (files.length) {
var file = files[0];
if (/(gif|jpeg|png|jpg|bmp)$/.test(file.type)) {
compress(file, 1920, updateImg);
}
}
});
// 压缩图片 & 上传
function compress(data, maxWidth, callbackFunc) {
var reader = new FileReader();
reader.readAsDataURL(data)
reader.onload = function(e) {
//新建一个img标签(还没嵌入DOM节点)
var image = new Image()
image.src = e.target.result;
image.onload = function() {
var percent = 1,
canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
imageWidth, //压缩后图片的大小
imageHeight,
result = '';
// 计算压缩比
if(image.width > maxWidth) {
percent = maxWidth / image.width;
}
// 对图片进行等比压缩
imageWidth = image.width * percent;
imageHeight = image.height * percent;
canvas.width = imageWidth;
canvas.height = imageHeight;
context.drawImage(image, 0, 0, imageWidth, imageHeight);
result = canvas.toDataURL('image/jpeg');
callbackFunc(dataURLtoBlob(result), data.name);
}
}
}
/**
* base64转二进制
* @param dataurl
* @returns {Blob}
*/
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while (n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
}
/**
* 上传到后端
* @param res
* @param filename
*/
function updateImg(res, filename) {
$('.js-upload-local').val();
var imageFile = new FormData();
imageFile.append("post_image", res, filename);
$.ajax({
type: "post",
url: './index.php',
data: imageFile,
dataType: 'json',
processData: false, //tell jQuery not to process the data
contentType: false, //tell jQuery not to set contentType
beforeSend: function (XMLHttpRequest) {
// 加载loading信息
},
success: function (res) {
console.log(res)
},
complete: function () {
// 取消loading效果
},
error: function (res) {
console.log(res)
}
});
}
</script>
</html>
后记
如果不需要前端canvas压缩,或者兼容性不允许,可以直接使用后端的代码。
扩展学习(blob,二进制,base64)
代码语言:javascript复制<!DOCTYPE HTML>
<html>
<meta charset="utf-8">
<head>
</head>
<body>
// multiple属性可以让用户能选择多个文件
<input id="myfiles" multiple type="file">
</body>
<script>
var pullfiles = function(){
// 获取到input元素
var fileInput = document.querySelector("#myfiles");
// 所有type属性(attribute)为file的 <input> 元素都有一个files属性(property),用来存储用户所选择
var files = fileInput.files;
// 普通方式上传二进制文件
if (files[0]) {
uploadImg(files[0])
}
// 将file生成blob链接,可用于本地预览,缩略图
if (files[0]) {
var blobUrl = URL.createObjectURL(files[0]);
console.log('blob链接:', blobUrl)
}
// 生成data:base64的字符串
if (files[0]) {
var reader = new FileReader();
reader.readAsDataURL(files[0])
reader.onload = function (e) {
// 这里操作生成base64字符串
var base64str = e.target.result;
// 上传二进制文件(由base64转换成的二进制)
uploadImg(dataURLtoBlob(base64str))
}
}
}
// 设置change事件处理函数
document.querySelector("#myfiles").onchange=pullfiles;
/**
* base64转二进制
* @param dataurl
* @returns {Blob}
*/
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while (n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
}
/**
* 上传文件
*/
function uploadImg(files) {
var formData = new FormData();
formData.append('username', 'abc123');
formData.append('post_image', files);
fetch('http://localhost:81/test/mozilla-test/upload.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));
}
</script>
</html>