项目需求:
项目一开始使用第三方cdn,七牛服务器上传图片,但是因为项目设计到很多身份证,营业执照等照片的上传,怕上传到这些cdn上面,消息泄露,所以将上传到第三方,改为上传到本地,七牛上传在之前的文章中有提到过,这里就不细说了,那么,接下了,我给大家讲一下laravel上传图片到本地的代码实现吧。
首先你需要了解plupload.js的相关知识:
define(['jquery', 'plupload', 'utils', 'locale'], function ($, plupload, utils, Locale) {
var sdk = {};
var defaultSetting = {
multi_selection: false,
file_data_name: 'image',
flash_swf_url: '/static/libs/plupload/Moxie.swf',
auto_start: true,
url: '/service/cdn/upload',
headers: {
'X-XSRF-TOKEN': $.cookie('XSRF-TOKEN')
}
};
sdk.uploader = function (settings) {
var options = {};
plupload.extend(options, defaultSetting, settings);
var backupFileUploadedHandler = options.init && options.init.FileUploaded;
if (backupFileUploadedHandler) {
options.init.FileUploaded = function () {};
}
var uploadedErrorHandler = options.init && options.init.Error;
if (uploadedErrorHandler) {
options.init.Error = function () {};
}
var uploader = new plupload.Uploader(options);
// bind 'FilesAdded' event
uploader.bind('FilesAdded', function (up, files) {
var autoStart = up.getOption && up.getOption('auto_start');
autoStart = autoStart || (up.settings && up.settings.auto_start);
if (autoStart) {
setTimeout(function (){
up.start();
}, 0);
}
// Reposition Flash/Silverlight
up.refresh();
});
// bind 'FileUploaded' event
uploader.bind('FileUploaded', (function (fileUploadedHandler) {
return function (up, file, info) {
var response = $.parseJSON(info.response);
if (response.status == 'SUCCESS') {
if (fileUploadedHandler) {
fileUploadedHandler(up, file, response.body);
}
} else {
utils.showMessage(Locale.t('errors.upload_image_failed'));
}
};
})(backupFileUploadedHandler));
uploader.bind('Error', (function(uploadedErrorHandler) {
return function(up, err) {
var errTip = '';
var file = err.file;
if (file) {
switch (err.code) {
case plupload.FAILED:
errTip = Locale.t('errors.upload_image_failed');
break;
case plupload.FILE_SIZE_ERROR:
var max_file_size = up.getOption && up.getOption('max_file_size');
max_file_size = max_file_size || (up.settings && up.settings.max_file_size);
errTip = Locale.t('errors.img_size_error', {size: max_file_size});
break;
case plupload.FILE_EXTENSION_ERROR:
errTip = Locale.t('errors.img_type_error');
break;
default:
errTip = Locale.t('errors.upload_image_failed');
break;
}
}
uploadedErrorHandler(up, err, errTip);
};
})(uploadedErrorHandler));
uploader.init();
return uploader;
};
return sdk;
});
该js复写了plupload.js,在qiniu上传的js基础上进行的修改,
在自定义的js文件中使用uploader.js,
var providerLicenseUpload = Uploader.uploader({
runtimes: 'html5,flash,html4',
browse_button: 'provider-license-upload',
max_file_size: '1mb',
dragdrop: false,
auto_start: true,
filters: {
mime_types: [
{
title : 'Image files',
extensions : 'jpg,jpeg,gif,png'
}
]
},
init: {
FileUploaded: function (up, file, info) {
$('#provider-license-img').attr('src', info.url).closest('.license-wrapper').show();
$('#provider-license').val(info.key);
},
Error: function (up, err, errTip) {
utils.showMessage(errTip);
}
}
});
后台上传图片的代码:
public function uploadImage(Request $request)
{
$this->validate($request, [
'image' => 'required|file|image',
]);
$file = $request->file('image');
$key = $request->input('key');
$result = app('cdn')->save($file, $key);
if (!$result) {
return response()->json(format_json_failed('request_failed'));
}
$data = [
'url' => $result['url'],
'key' => $result['key'],
];
return response()->json(format_json_success($data));
}
public function save($file, $key = null)
{
if ($key === null) {
$key = md5($file->path() . time() . mt_rand(1, 1000)) .'.' . $file->extension();
}
$path = Storage::disk($this->disk)->putFileAs($this->folder, $file, $key);
if ($path) {
return [
'url' => $this->domain . '/' . $path,
'key' => $key,
];
}
return false;
}
当你以为这样就结束的时候,你错了,有需求需要在个人资料编辑的时候,调用微信的相机。。。微信的相机耶,好高大上的样子
显然,这里需要使用微信的jssdk,具体使用方法,请查看,微信jssdk说明文档:
https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
好的下面开始进入正题:
案例:图片上传到微信(该上传为临时上传,只能在微信服务器中保存3天),那么我们需要再次下载到本地,持久化存储。
ok,代码写起来:
(1)微信jssdk上传头像:
define(['jquery', 'wechat_jssdk', 'utils', 'locale'], function ($, wx, utils, Locale) {
var shareConfig = APP_CONFIG.wechat,
image = {};
var _initConfig = function() {
if (shareConfig.is_wechat == 0) {
return;
}
wx.config({
debug: (shareConfig.debug == 1) ? true : false,
appId: shareConfig.appId,
timestamp: shareConfig.timestamp,
nonceStr: shareConfig.nonceStr,
signature: shareConfig.signature,
jsApiList: [
'chooseImage',
'uploadImage'
]
});
};
var _initUpload = function(callback) {
wx.ready(function() {
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: function (res) {
var localIds = res.localIds;
wx.uploadImage({
localId: localIds[0],
isShowProgressTips: 1,
success: function (res) {
var serverId = res.serverId;
$.ajax({
url: '/service/cdn/download',
data: {
media_id: serverId
},
type: 'post'
}).done(function(response) {
if (response.status == 'SUCCESS') {
var image = response.body;
callback(image);
} else {
utils.showMessage(Locale.t('errors.system_error'));
}
});
}
});
}
});
});
};
return {
initConfig: _initConfig,
initUpload: _initUpload
};
});
WeChatUpload.initConfig();
$('#avatar').on('click', function() {
WeChatUpload.initUpload(function(image) {
var imageUrl = image['url'];
var imageKey = image['key'];
if (imageUrl && imageKey) {
$('#avatar-input').val(imageKey);
$('#avatar').attr('src', imageUrl);
}
});
});
initConfig执行一次,initUpload每次点击头像时执行,jssdk是很简单的,那么这里最重要的是该如何在上传到微信服务器,然后从微信服务器下载,并且保存到本地的指定目录呢?OK,让我来用代码来征服你吧。。。让你爱上我。。。
public function downloadImage(Request $request)
{
$mediaId = $request->input('media_id');
$media = app('wechat.mp')->getMedia($mediaId);
$key = md5($mediaId . time() . mt_rand(1, 1000)) . '.jpg';
// save image
$filePath = config('filesystems.disks.ugc.root', base_path('www-ugc')) . '/image/' . $key;
$file = fopen($filePath, 'w+');
fwrite($file, $media);
fclose($file);
if(!file_exists($filePath))
{
return response()->json(format_json_failed('request_failed'));
}
$data = [
'key' => $key,
'url' => app('cdn')->getResourceUrl($key)
];
return response()->json(format_json_success($data));
}
好吧,就这样,这里要解释一下getMedia这个方法,参数是使用jssdk上传之后返回的serverId,该方法其实是请求一个微信的连接:
http请求方式: GET http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID 请求示例(示例为通过curl命令获取多媒体文件) curl -I -G "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID"这个在微信jssdk文档中有提过,记住一定要先看该文档哟,一定,一定,一定哟~~~该方法神奇的给你下载图片,但是在代码里是给你返回的二进制流,这个时候,寡人百度了又百度,谷歌了又谷歌,找到了php写入文件流的方法,保存图片,那就是神奇的:
fopen+fwrite+fclose,ok就这样,可以将二进制数据,保存到本地,(其实图片就是二进制数据,只是变现格式不一样,你把图片用sublime打开,就是一串字符集编码)。
好吧,到这里也许已经很多人爱上了我,但是,但是,此时此刻,我爱上了我的leader,(本人大学刚毕业。。。leader已n年工作经验),好吧,不得不爱上他,来开开他的后台下载微信图片,并保存到本地的方法吧。。。
if (!function_exists('mime_type_to_image_type')) {
function mime_type_to_image_type($mimeType)
{
$mimeTypes = [
'image/gif' => IMAGETYPE_GIF,
'image/jpeg' => IMAGETYPE_JPEG,
'image/png' => IMAGETYPE_PNG,
'application/x-shockwave-flash' => IMAGETYPE_SWF,
'image/psd' => IMAGETYPE_PSD,
'image/bmp' => IMAGETYPE_BMP,
'image/tiff' => IMAGETYPE_TIFF_II,
'image/jp2' => IMAGETYPE_JP2,
'image/iff' => IMAGETYPE_IFF,
'image/vnd.wap.wbmp' => IMAGETYPE_WBMP,
'image/xbm' => IMAGETYPE_XBM,
'image/vnd.microsoft.icon' => IMAGETYPE_ICO,
];
return array_get($mimeTypes, $mimeType, false);
}
}
//复写了wechat.class.php中的getMedia方法
public function getImage($mediaId)
{
if (!$this->access_token && !$this->checkAuth()) {
return false;
}
$url = self::API_URL_PREFIX . self::MEDIA_GET_URL;
$query = [
'access_token' => $this->access_token,
'media_id' => $mediaId,
];
$client = new Client();
$response = $client->get($url, [
'query' => $query,
]);
if ($response->getStatusCode() != 200) {
return false;
}
$result = [];
$contentType = $response->getHeader('Content-Type');
$imageType = mime_type_to_image_type(array_shift($contentType));
if ($imageType !== false) {
$result['image_type'] = $imageType;
}
$result['data'] = $response->getBody();
return $result;
}
public function downloadImage(Request $request)
{
$mediaId = $request->input('media_id');
// $mediaId = '_Eb3yhich_NAfrE10lhEmuLl6WCU2ICTD7SRZGFtNP2FY4kX63Q-QLWi1tJ7aVdz';
$imageResult = app('wechat.mp')->getImage($mediaId);
if ($imageResult === false) {
return response()->json(format_json_failed('request_failed'));
}
$imageType = array_get($imageResult, 'image_type');
$extension = '.jpg';
if ($imageType) {
$extension = image_type_to_extension($imageType);
}
$key = md5($mediaId . time() . mt_rand(1, 1000)) . $extension;
$path = 'temp/' . $key;
// save image to temp folder
$result = Storage::disk('local')->put($path, $imageResult['data']);
if (!$result) {
return response()->json(format_json_failed('request_failed'));
}
// save image to cdn
$filePath = rtrim(config('filesystems.disks.local.root'), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $path;
$file = new File($filePath);
$cdn = app('cdn');
$data = $cdn->save($file, $key);
if ($data) {
return response()->json(format_json_success($data));
} else {
return response()->json(format_json_failed('request_failed'));
}
}
这里和上面的upload使用了同一套save方法,个人觉得并没有什么不同,都是保存图片到本地,这样非得作,但是我还是深深的爱上了他。。。好吧,也许看了这么多代码,很多小朋友也不是很了解,如果有疑问,欢迎线下来找,我们互相探讨,不行我还有我leader,哈哈哈!!!