找到
295
篇与
易航
相关的结果
- 第 30 页
-
三个令人期待的CSS特性即将推出! 大家好,我是易航,今天给大家总结分享一下很 🐂🍺 的3个即将推出的 CSS 属性,等你看完,每一个都要喊:绝了! {callout color="var(--theme)"} 声明一下:这些特性现在基本都不好使,就算有的高级浏览器支持了,也只是个例。可以期待一下以后 ~ {/callout} 前端CSS图片 一、@container @container 是一个容器查询方法,正如它的名字一样,它是用来支持根据当前元素所在容器的大小来进行动态修改添加样式的,这跟 @media 基于视口大小是不一样的。 来举个🌰 先创建一个侧边栏和一个主内容 <body> <aside class="sidebar"> <div class="card"> <h4>侧边栏</h4> <p> To the world you may be one person, but to one person you may be the world. </p> </div> </aside> <main class="content"> <div class="card"> <h4>主内容</h4> <p> To the world you may be one person, but to one person you may be the world. </p> </div> </main> </body>让这两个元素横向布局,且侧边栏宽度占 30% ,主内容宽度占 70% body { display: flex; color: white; } h4 { color: black; } .sidebar { width: 30%; } .content { width: 70%; background: #f0f5f9; /* 给个底色,与侧边栏区分 */ } .card { background: lightpink; box-shadow: 3px 10px 20px rgba(0, 0, 0, 0.2); border-radius: 8px; }目前为止是这样的效果: 图片 现在我们发现主内容这块儿空间很富余,便想改变一下标题和内容文字的布局,此时就可以用上 @container 了,直接让主内容在当前容器宽度大于 400px 时变成横向布局 @container (min-width: 400px) { .content .card { display: flex; } }此时效果如下: 图片 是不是很酷 😎 基于这点,还想到了一个之前我做过的需求中很头疼的需求,就是字体大小随着容器宽高的改变而动态改变,如果支持了这个特性,那这个需求也就很简单了 二、object-view-box object-view-box 属性就类似于 SVG 中的 viewBox 属性。它允许您使用一行 CSS 来平移、缩放、裁剪 图像。 我们就对这张图来动动刀子 图片 加一行代码 .crop { object-view-box: inset(10% 50% 35% 5%); }实现的效果就是这样: 图片 跟原图对比一下就是这样: 图片 除了简单的裁剪,我们还能基于它实现一些好玩的效果,例如: 图片 三、animation-timeline animation-timeline 相比前两个就更好玩了!它允许我们基于容器滚动的进度来对动画进行处理,简而言之就是页面滚动了百分之多少,动画就执行百分之多少。而且动画也能根据页面倒着滚动而倒着播放 .shoes { animation-name: Rotate; animation-duration: 1s; animation-timeline: scrollTimeline; } @scroll-timeline scrollTimeline { source: selector('#container'); orientation: "vertical"; } @keyframes Rotate { from { transform: translate(-200px, -200px) rotate(0deg); } to { transform: translate(100vw, 100vh) rotate(720deg); } }使用起来很简单,就是在本身的基础动画上,新增一个 animation-timeline 属性即可,我们也可以对这个 timeline 定义是基于哪个容器,滚动方向是水平还是竖直 大致效果就是: 图片 最后 这三个 CSS 新特性,你们最喜欢哪个?有没有想到一些比较实用的场景,欢迎在评论区分享~
-
PHP实现客户端HTTPS协议强制退回到HTTP状态 前言 网上有很多HTTP升级为HTTPS的方法,但是让客户端所有用户从HTTPS退回HTTP的有效方法却很少。为了自己站点能够退回HTTP,我也是折腾了很长时间才想出来这个方法。 废话不多说,直接上本人自己研究出来的方法 HTTPS退回HTTP图片 实现方法 首先要在站点部署一个错误的SSL证书,如果无证书或证书正确会陷入301跳转循环! 然后在网站的入口文件最上方放入一段代码 <?php if ($_SERVER['HTTPS'] == 'on') { if ($_COOKIE['HTTPS']) { ?> <script type="text/javascript"> var targetProtocol = "http:"; if (window.location.protocol != targetProtocol) { window.location.href = targetProtocol + window.location.href.substring(window.location.protocol.length) } </script> <?php exit('请使用http协议访问本站'); } if (!$_COOKIE['HTTPS']) { setcookie("HTTPS", true, time() + 3600); } sleep(1); $url = "http://" . $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"]; header('HTTP/1.1 301 Moved Permanently'); header('Location:' . $url); } ?>原理总结 浏览器检测到错误的SSL证书就不会让站点强制HTTPS,没有了强制HTTPS我们就可以进行跳转到HTTP协议状态,但是只用301重定向这种方式会无限循环,导致浏览器检测到301重定向过多返回错误码。那么我们就只把301重定向给到搜索引擎来看,这种代码的写法不用专门检测是不是搜索引擎,避免误判之类的情况,给到用户这边用JS同样进行无感跳转网页。
-
PHP如何有效缩短接口响应时间 前言 中医讲对症下药,我们也需要定位到哪个位置消耗了多长时间,具体到每个位置去进行优化,这样我们就需要知道请求某个接口,每个流程所需要的时间,这样一般都是由日志来查看的。 本文从两点来说一下 PHP图片 一、缩短数据库操作耗时 这里以TP5框架为例,TP5开启日志的情况,会很详细的记录程序运行所使用的时间,来看下一个很常见的请求接口日志: [运行时间:1.207150s] [吞吐率:0.83req/s] [内存消耗:2,881.48kb] [文件加载:68] [ info ] [ LANG ] D:\webroot\www\thinkphp\lang\zh-cn.php [ info ] [ ROUTE ] array ( 'type' => 'module', 'module' => array ( 0 => 'admin', 1 => 'Order', 2 => 'getConfirmOrder', ), ) [ info ] [ HEADER ] array ( 'content-type' => '', 'content-length' => '0', 'x-original-url' => '/admin/Order/getConfirmOrder', 'origin' => 'http://www.***.com', 'x-requested-with' => 'XMLHttpRequest', 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36', 'referer' => 'http://www.***.com/admin/baby/index.html', 'host' => 'www.***.com', 'cookie' => 'security_session_verify=d66a8d5d0b34e8c6431d6bc554fbf24c; PHPSESSID=c2nph8nll013k9cu95l3a6d8h1', 'accept-language' => 'zh-CN,zh;q=0.9', 'accept-encoding' => 'gzip, deflate', 'accept' => 'application/json, text/javascript, */*; q=0.01', 'connection' => 'keep-alive', ) [ info ] [ PARAM ] array ( ) [ info ] [ SESSION ] INIT array ( 'prefix' => 'admin', 'type' => '', 'auto_start' => true, ) [ info ] [ DB ] INIT mysql [ info ] [ RUN ] app\admin\controller\Order->getConfirmOrder[ D:\webroot\www.***.com\application\admin\controller\Order.php ] [ info ] [ LOG ] INIT File [ sql ] [ DB ] CONNECT:[ UseTime:1.056759s ] mysql:host=localhost;port=3306;dbname=***;charset=utf8 [ sql ] [ SQL ] SHOW COLUMNS FROM `gmy_kanban` [ RunTime:0.011518s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` ORDER BY `kan_id` DESC [ RunTime:0.000676s ] [ sql ] [ SQL ] SHOW COLUMNS FROM `gmy_auth` [ RunTime:0.009073s ] [ sql ] [ SQL ] SELECT * FROM `gmy_auth` [ RunTime:0.002334s ] [ sql ] [ SQL ] SHOW COLUMNS FROM `gmy_admin` [ RunTime:0.010282s ] [ sql ] [ SQL ] SELECT * FROM `gmy_admin` WHERE `user_name` = 'admin99sj' LIMIT 1 [ RunTime:0.001304s ] [ sql ] [ SQL ] SHOW COLUMNS FROM `gmy_role` [ RunTime:0.010645s ] [ sql ] [ SQL ] SELECT * FROM `gmy_role` WHERE `role_id` IN (91) LIMIT 1 [ RunTime:0.000684s ] [ sql ] [ SQL ] SELECT * FROM `gmy_role` WHERE `role_id` IN (196) LIMIT 1 [ RunTime:0.000588s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/yingxiao' LIMIT 1 [ RunTime:0.000677s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/xingzheng' LIMIT 1 [ RunTime:0.000469s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/kefu' LIMIT 1 [ RunTime:0.000447s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/yanguangshi' LIMIT 1 [ RunTime:0.000519s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/shichang' LIMIT 1 [ RunTime:0.000443s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/wangluo' LIMIT 1 [ RunTime:0.000463s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/table' LIMIT 1 [ RunTime:0.000506s ] [ sql ] [ SQL ] SELECT * FROM `gmy_auth` WHERE `auth_des` LIKE '%order/getconfirmorder%' AND ( fen_id<>9 ) LIMIT 1 [ RunTime:0.001173s ]从运行时间来看1.2秒左右,1秒左右的时间对于人的正常感应来说已经能够感知到慢了,往下看,我们会看到具体的每个流程所使用的时间,首先是数据库连接时间:1.056759s,接下来是一些数据表查询所用的时间都很少,都在1毫秒左右,可以忽略不计。那么这里最主要的症结就是连接时间,为什么数据库连接要这么久呢?来看一下连接的参数: mysql:host=localhost;port=3306;dbname=***;charset=utf8;优化:数据库连接不要使用localhost,改成127.0.0.1 我们试下把数据库主机host改成127.0.0.1,再来测试下,看日志 [运行时间:0.115078s] [吞吐率:8.69req/s] [内存消耗:481.07kb] [文件加载:66] [ info ] [ LANG ] D:\webroot\www.***.com\thinkphp\lang\zh-cn.php [ info ] [ ROUTE ] array ( 'type' => 'module', 'module' => array ( 0 => 'admin', 1 => 'order', 2 => 'getConfirmOrder', ), ) [ info ] [ HEADER ] array ( 'content-type' => '', 'content-length' => '0', 'x-original-url' => '/admin/order/getConfirmOrder', 'sec-fetch-dest' => 'document', 'sec-fetch-user' => '?1', 'sec-fetch-mode' => 'navigate', 'sec-fetch-site' => 'none', 'upgrade-insecure-requests' => '1', 'sec-ch-ua-platform' => '"Android"', 'sec-ch-ua-mobile' => '?1', 'sec-ch-ua' => '" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"', 'user-agent' => 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36', 'host' => 'www.***.com', 'cookie' => 'security_session_verify=435345dacd7ada35e3d0ef60f48a169f; PHPSESSID=687tptp9lbd3aip2gph1uth450', 'accept-language' => 'zh-CN,zh;q=0.9', 'accept-encoding' => 'gzip, deflate, br', 'accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'connection' => 'keep-alive', 'cache-control' => 'max-age=0', ) [ info ] [ PARAM ] array ( ) [ info ] [ SESSION ] INIT array ( 'prefix' => 'admin', 'type' => '', 'auto_start' => true, ) [ info ] [ DB ] INIT mysql [ info ] [ RUN ] app\admin\controller\Order->getConfirmOrder[ D:\webroot\www.***.com\application\admin\controller\Order.php ] [ info ] [ LOG ] INIT File [ sql ] [ DB ] CONNECT:[ UseTime:0.001759s ] mysql:host=127.0.0.1;port=3306;dbname=***;charset=utf8 [ sql ] [ SQL ] SHOW COLUMNS FROM `gmy_kanban` [ RunTime:0.014265s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` ORDER BY `kan_id` DESC [ RunTime:0.000765s ] [ sql ] [ SQL ] SHOW COLUMNS FROM `gmy_auth` [ RunTime:0.009900s ] [ sql ] [ SQL ] SELECT * FROM `gmy_auth` [ RunTime:0.001970s ] [ sql ] [ SQL ] SHOW COLUMNS FROM `gmy_admin` [ RunTime:0.012493s ] [ sql ] [ SQL ] SELECT * FROM `gmy_admin` WHERE `user_name` = 'admin99sj' LIMIT 1 [ RunTime:0.000990s ] [ sql ] [ SQL ] SHOW COLUMNS FROM `gmy_role` [ RunTime:0.008893s ] [ sql ] [ SQL ] SELECT * FROM `gmy_role` WHERE `role_id` IN (91) LIMIT 1 [ RunTime:0.000611s ] [ sql ] [ SQL ] SELECT * FROM `gmy_role` WHERE `role_id` IN (196) LIMIT 1 [ RunTime:0.000652s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/yingxiao' LIMIT 1 [ RunTime:0.000524s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/xingzheng' LIMIT 1 [ RunTime:0.000387s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/kefu' LIMIT 1 [ RunTime:0.000376s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/yanguangshi' LIMIT 1 [ RunTime:0.000338s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/shichang' LIMIT 1 [ RunTime:0.000346s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/wangluo' LIMIT 1 [ RunTime:0.000362s ] [ sql ] [ SQL ] SELECT * FROM `gmy_kanban` WHERE `auth_des` = 'index/table' LIMIT 1 [ RunTime:0.000337s ] [ sql ] [ SQL ] SELECT * FROM `gmy_auth` WHERE `auth_des` LIKE '%order/getconfirmorder%' AND ( fen_id<>9 ) LIMIT 1 [ RunTime:0.000816s ]再看运行时间只有0.11秒,大约快了10倍,我们看下具体哪里快了,看连接数据库的时间0.001759s,这比改之前快了几百倍! 二、php性能优化 接下来我们来看下php性能的优化,Opcache和JIT,JIT在我的实际项目中体验是,开启JIT可以大略缩短100ms的时间,也就是0.1秒,现在php8都说性能很好,但是如果你不开启JIT,那么性能是体会不到的。 我们来看下,php5.6下如何开启Opcache,php5.6版本默认是安装过Opcache扩展的,在ext下找到php_opcache.dll,php8是需要自己手动下载安装这个扩展的,具体方法需要你自己去百度,接下来就是php.ini配置了,公众号里前面文章有讲过,感兴趣的可以去看看,这里不在多说,只是把opcache跑起来,配置如下: zend_extension = "D:\webroot\zzidcconf\php\php5.6\ext\php_opcache.dll" ; Determines if Zend OPCache is enabled opcache.enable=1 ; Determines if Zend OPCache is enabled for the CLI version of PHP opcache.enable_cli=1 ; The OPcache shared memory storage size. opcache.memory_consumption=128 ; The amount of memory for interned strings in Mbytes. opcache.interned_strings_buffer=8 ; The maximum number of keys (scripts) in the OPcache hash table. ; Only numbers between 200 and 100000 are allowed. opcache.max_accelerated_files=2000把上边的配置项该开启的开启,该添加的添加,有的需要修改的修改下,然后重启服务器web环境,测试phpinfo,如果看到下图,则代表opcache启动成功。 PHP配置图片 总结 本篇文章从两个方面谈了下如何提升接口响应速度,一个是数据库时间,一个是php自身的运行时间,不是特别复杂的点,如果你碰到了同样的情况,可以立马使用改善。最后,告诫自己,写代码的时候,一定要尽可能的一遍成,不要想着后期再来完善。一起加油!
-
Joe主题解决友链介绍高度不一BUG Joe 解决友情链接页面的友链网站介绍太长导致高度不整齐的问题,如下图 Joe主题图片 解决方法 修改 joe.global.min.css 文件,搜索关键词 desc{margin-right:10px} 。文件路径:主题目录/assets/css/joe.global.min.css 把以下代码加入到关键词里的 10px 后面 ;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;CSS 修改完成后的样子 .desc{margin-right:10px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}修改完后网站介绍太长会直接以...的方式省略掉,如下图 Joe主题图片
-
Typecho自动生成站点地图插件最新版(支持 V1.1 和 V1.2) 前言 关于 sitemap 的作用,做过网站 SEO 的站长应该都挺熟悉的吧。 Sitemap,又叫站点地图。站点地图相当于一个网站目录,它可以更直观地向用户展示网站的结构和网站中文章的内容,方便用户浏览和使用。另外,方便百度蜘蛛抓取网站页面,通过站点地图获取站点内的信息,可以增加站点内的文章收藏。 Typecho主题为了简化代码,多数没有自带sitemap,这对想爬取我们网站的蜘蛛很不友好,在花费些时间整理出来后,免费开源给大家使用,功能全且小巧,最最重要的是不占用大内存,自动识别并收录整合到自己网站的新页面。 功能介绍 生成 sitemap 包含 首页、独立页面、分类、标签、文章 推送百度搜索资源平台,目前仅支持普通收录推送 推送最新文章会获取最新20篇文章,获取不到会报错,使用推送全部文章即可 API说明 参数名参数值接口功能sitemapupdate更新sitemappushmain推送核心文章pushall推送全部文章pushnew推送最新文章tokenAPI token插件中的API token插件截图 Typecho自动生成网站Sitemap插件图片 Typecho自动生成网站Sitemap插件图片 更新说明 禁用插件后删除再更新!更新后插件目录设置 777 权限 版本历史 19be6b 19be6b 19be6b 19be6b 19be6b 19be6b ed4014 PS:开个玩笑哈哈,作者没有删库跑路 下载插件 隐藏内容,请前往内页查看详情
-
PHP实现文件上传和下载实例详解 前言 本文主要介绍用PHP上传和下载文件的例子。详细全面地阐述了文件上传的需求分析和功能实现,同时给出了使用代码。有需要的朋友可以参考一下。 在PHP中上传和下载文件是一个基本的功能。一般网站或多或少都会有这样的要求。当然也不是说所有文件都能上传,所以这个网络太不安全了。因为接触php的时间不长,所以今天的写作和练习只是一个公开的记录。 效果图 PHP实现文件上传和下载图片 PHP实现文件上传和下载图片 首先是封装好的图片类(缩放及生成水印) 1、GDBasic.php <?php /** * GDBasic.php * description GD基础类 */ namespace test\Lib; class GDBasic { protected static $_check = false; //检查服务器环境中gd库 public static function check() { //当静态变量不为false if (static::$_check) { return true; } //检查gd库是否加载 if (!function_exists("gd_info")) { throw new \Exception('GD is not exists'); } //检查gd库版本 $version = ''; $info = gd_info(); if (preg_match("/\\d+\\.\\d+(?:\\.\\d+)?/", $info["GD Version"], $matches)) { $version = $matches[0]; } //当gd库版本小于2.0.1 if (!version_compare($version, '2.0.1', '>=')) { throw new \Exception("GD requires GD version '2.0.1' or greater, you have " . $version); } self::$_check = true; return self::$_check; } }2、Image.php <?php /** * Image.php * description 图像类 */ namespace test\Lib; require_once 'GDBasic.php'; class Image extends GDBasic { protected $_width; protected $_height; protected $_im; protected $_type; protected $_mime; protected $_real_path; public function __construct($file) { //检查GD库 self::check(); $imageInfo = $this->createImageByFile($file); $this->_width = $imageInfo['width']; $this->_height = $imageInfo['height']; $this->_im = $imageInfo['im']; $this->_type = $imageInfo['type']; $this->_real_path = $imageInfo['real_path']; $this->_mime = $imageInfo['mime']; } /** * 根据文件创建图像 * @param $file * @return array * @throws \Exception */ public function createImageByFile($file) { //检查文件是否存在 if (!file_exists($file)) { throw new \Exception('file is not exits'); } //获取图像信息 $imageInfo = getimagesize($file); $realPath = realpath($file); if (!$imageInfo) { throw new \Exception('file is not image file'); } switch ($imageInfo[2]) { case IMAGETYPE_GIF: $im = imagecreatefromgif($file); break; case IMAGETYPE_JPEG: $im = imagecreatefromjpeg($file); break; case IMAGETYPE_PNG: $im = imagecreatefrompng($file); break; default: throw new \Exception('image file must be png,jpeg,gif'); } return array( 'width' => $imageInfo[0], 'height' => $imageInfo[1], 'type' => $imageInfo[2], 'mime' => $imageInfo['mime'], 'im' => $im, 'real_path' => $realPath, ); } /** * 缩略图 * @param int $width 缩略图高度 * @param int $height 缩略图宽度 * @return $this * @throws \Exception */ public function resize($width, $height) { if (!is_numeric($width) || !is_numeric($height)) { throw new \Exception('image width or height must be number'); } //根据传参的宽高获取最终图像的宽高 $srcW = $this->_width; $srcH = $this->_height; if ($width <= 0 || $height <= 0) { $desW = $srcW; //缩略图高度 $desH = $srcH; //缩略图宽度 } else { $srcP = $srcW / $srcH; //宽高比 $desP = $width / $height; if ($width > $srcW) { if ($height > $srcH) { $desW = $srcW; $desH = $srcH; } else { $desH = $height; $desW = round($desH * $srcP); } } else { if ($desP > $srcP) { $desW = $width; $desH = round($desW / $srcP); } else { $desH = $height; $desW = round($desH * $srcP); } } } //PHP版本小于5.5 if (version_compare(PHP_VERSION, '5.5.0', '<')) { $desIm = imagecreatetruecolor($desW, $desH); if (imagecopyresampled($desIm, $this->_im, 0, 0, 0, 0, $desW, $desH, $srcW, $srcH)) { imagedestroy($this->_im); $this->_im = $desIm; $this->_width = imagesx($this->_im); $this->_height = imagesy($this->_im); } } else { if ($desIm = imagescale($this->_im, $desW, $desH)) { $this->_im = $desIm; $this->_width = imagesx($this->_im); $this->_height = imagesy($this->_im); } } return $this; } /** * 根据百分比生成缩略图 * @param int $percent 1-100 * @return Image * @throws \Exception */ public function resizeByPercent($percent) { if (intval($percent) <= 0) { throw new \Exception('percent must be gt 0'); } $percent = intval($percent) > 100 ? 100 : intval($percent); $percent = $percent / 100; $desW = $this->_width * $percent; $desH = $this->_height * $percent; return $this->resize($desW, $desH); } /** * 图像旋转 * @param $degree * @return $this */ public function rotate($degree) { $degree = 360 - intval($degree); $back = imagecolorallocatealpha($this->_im, 0, 0, 0, 127); $im = imagerotate($this->_im, $degree, $back, 1); imagesavealpha($im, true); imagedestroy($this->_im); $this->_im = $im; $this->_width = imagesx($this->_im); $this->_height = imagesy($this->_im); return $this; } /** * 生成水印 * @param file $water 水印图片 * @param int $pct 透明度 * @return $this */ public function waterMask($water = '', $pct = 60) { //根据水印图像文件生成图像资源 $waterInfo = $this->createImageByFile($water); imagecopymerge(); //销毁$this->_im $this->_im = $waterInfo['im']; $this->_width = imagesx($this->_im); $this->_height = imagesy($this->_im); return $this; } /** * 图片输出 * @return bool */ public function show() { header('Content-Type:' . $this->_mime); if ($this->_type == 1) { imagegif($this->_im); return true; } if ($this->_type == 2) { imagejpeg($this->_im, null, 80); return true; } if ($this->_type == 3) { imagepng($this->_im); return true; } } /** * 保存图像文件 * @param $file * @param null $quality * @return bool * @throws \Exception */ public function save($file, $quality = null) { //获取保存目的文件的扩展名 $ext = pathinfo($file, PATHINFO_EXTENSION); $ext = strtolower($ext); if (!$ext || !in_array($ext, array('jpg', 'jpeg', 'gif', 'png'))) { throw new \Exception('image save file must be jpg ,png,gif'); } if ($ext === 'gif') { imagegif($this->_im, $file); return true; } if ($ext === 'jpeg' || $ext === 'jpg') { if ($quality > 0) { if ($quality < 1) { $quality = 1; } if ($quality > 100) { $quality = 100; } imagejpeg($this->_im, $file, $quality); } else { imagejpeg($this->_im, $file); } return true; } if ($ext === 'png') { imagepng($this->_im, $file); return true; } } }style样式不是重点,先不放了 然后是ajax类封装的文件:ajax.js let $ = new class { constructor() { this.xhr = new XMLHttpRequest(); this.xhr.onreadystatechange = () => { if (this.xhr.readyState == 4 && this.xhr.status == 200) { // process response text let response = this.xhr.responseText; if (this.type == "json") { response = JSON.parse(response); } this.callback(response); } } } get(url, parameters, callback, type = "text") { // url = test.php?username=zhangsan&age=20 // parameters = {"username": "zhangsan", "age": 20} let data = this.parseParameters(parameters); if (data.length > 0) { url += "?" + data; } this.type = type; this.callback = callback; this.xhr.open("GET", url, true); this.xhr.send(); } post(url, parameters, callback, type = "text") { let data = this.parseParameters(parameters); this.type = type; this.callback = callback; this.xhr.open("POST", url, true); this.xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); this.xhr.send(data); } parseParameters(parameters) { // username=zhangsan&age=20 let buildStr = ""; for (let key in parameters) { let str = key + "=" + parameters[key]; buildStr += str + "&"; } return buildStr.substring(0, buildStr.length - 1); } };1、首页文件:index.php <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后!--> <title>文件上传和下载</title> <!-- Bootstrap --> <link href="style/css/bootstrap.min.css" rel="stylesheet"> <link href="style/css/site.min.css" rel="stylesheet"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> <style> .projects .thumbnail .caption { height: auto; max-width: auto; } .image { margin: 10px auto; border-radius: 5px; overflow: hidden; border: 1px solid #CCC; } .image .caption P { text-align: center; } </style> </head> <body> <!--导航栏--> <div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand hidden-sm" href="" onclick="_hmt.push(['_trackEvent', 'navbar', 'click', 'navbar-首页'])">XX网</a> </div> <div class="navbar-collapse collapse" role="navigation"> <ul class="nav navbar-nav"> <li class="hidden-sm hidden-md"> <a href="" target="_blank"></a> </li> <li> <a href="" target="_blank"></a> </li> </ul> </div> </div> </div> <!--导航栏结束--> <!--巨幕--> <div class="jumbotron masthead"> <div class="container"> <h1>文件上传下载</h1> <h2>实现文件的上传和下载功能</h2> <p class="masthead-button-links"> <form class="form-inline" onsubmit="return false;"> <div class="form-group"> <input type="search" class="form-control keywords" id="exampleInputName2" placeholder="输入搜索内容" name="keywords" value=""> <button class="btn btn-default searchBtn" type="submit">搜索</button> <button type="button" class="btn btn-primary btn-default" data-toggle="modal" data-target="#myModal"> 上传 </button> </div> </form> </p> </div> </div> <!--巨幕结束--> <!-- 模态框 --> <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <form class="form-inline" action="upload_class.php" method="post" enctype="multipart/form-data"> <input type="hidden" name="MAX_FILE_SIZE" value="10240000" /> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title" id="myModalLabel">上传图片</h4> </div> <div class="modal-body"> <p>选择图片:</p><input type="file" id="image" name="test_pic[]"> <br /> <p>图片描述:</p><textarea class="form-control" cols="75" name="description" id="info"></textarea> <br /><br /> <p> 是否添加水印: <select name="mark"> <option value="1">添加</option> <option value="0">不添加</option> </select> </p> <br /> <p> 图片宽度比例: <select name="scale"> <option value="800*600">800*600</option> <option value="600*450">600*450</option> <option value="400*300">400*300</option> </select> </p> </div> <div class="modal-footer"> <button type="submit" class="btn btn-default" name="submit" id="submit" onclick="show(this)">上传</button> <button type="reset" class="btn btn-primary">重置</button> </div> </div> </form> </div> </div> <!--模态框结束--> <div class="container projects"> <div class="projects-header page-header"> <h2>上传图片展示</h2> <p>将上传的图片展示在页面中</p> </div> <div class="row pic_body"> <!-- 使用js显示图片 --> </div> <!--分页--> <nav aria-label="Page navigation" style="text-align:center"> <ul class="pagination pagination-lg"> <!-- 使用js显示页码 --> </ul> </nav> </div> <footer class="footer container"> <div class="row footer-bottom"> <ul class="list-inline text-center"> <h4><a href="class.test.com" target="_blank">class.test.com</a> | XX网</h4> </ul> </div> </footer> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script src="style/js/jquery.min.js"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="style/js/bootstrap.min.js"></script> <script type="text/JavaScript"> function show(){ if(document.getElementById("image").value == ''){ alert('请选择图片'); } if(document.getElementById("info").value == ''){ alert('请输入图片描述'); } } </script> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"> </script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"> </script> <script src="style/js/Ajax.js"></script> <script> let pageNo = 1; let kws = ''; let searchBtn = document.getElementsByClassName('searchBtn')[0]; searchBtn.onclick = function() { let search = document.getElementsByClassName('keywords')[0]; let keywords = search.value; requestData(pageNo, keywords); kws = keywords; }; let requestPage = function(page) { requestData(page, kws); pageNo = page; }; let requestData = function(page_number, keywords) { let pagination = document.getElementsByClassName('pagination')[0]; let pic_body = document.getElementsByClassName('pic_body')[0]; pic_body.innerHTML = '<p style="text-align:center"><i class="fa fa-spinner fa-spin" style="font-size:24px"></i> 加载中...</p>'; $.get('search.php', { "page": page_number, "keywords": keywords }, function(res) { let divs = ''; if (res.code == 1) { // 请求成功 res.rows.forEach(function(item) { let div = '<div class="col-sm-6 col-md-3 col-lg-4"><div class="image"><a href="' + item .path + '" target="_blank"><img class="img-responsive" src="' + item.path + '" ></a><div class="caption"><p>' + item.info + '</p></div></div></div>'; divs += div; }); pic_body.innerHTML = divs; // 加载页码导航 // previous let previousBtn = ''; if (res.page_number == 1) { previousBtn = '<li class="page-item disabled"><a class="page-link" href="javascript:requestPage(' + (res.page_number - 1) + ');">Previous</a></li>'; } else { previousBtn = '<li class="page-item"><a class="page-link" href="javascript:requestPage(' + (res .page_number - 1) + ');">Previous</a></li>'; } // next let nextBtn = ''; if (res.page_total == res.page_number) { nextBtn = '<li class="page-item disabled"><a class="page-link" href="javascript:requestPage(' + (res.page_number + 1) + ');">Next</a></li>'; } else { nextBtn = '<li class="page-item"><a class="page-link" href="javascript:requestPage(' + ( res.page_number + 1) + ');">Next</a></li>' } let pages = previousBtn; for (let page = 1; page <= res.page_total; page++) { let active = ''; if (page == res.page_number) { active = 'active'; } pages += '<li class="page-item ' + active + '"><a class="page-link" href="javascript:requestPage(' + page + ');">' + page + '</a></li>'; } pages += nextBtn; pagination.innerHTML = pages; } }, 'json'); }; requestData(1, ''); </script> </body> </html>2、图片相关功能处理:upload_class.php <?php //接收传过来的数据 $description = $_POST['description']; //描述 $mark = $_POST['mark']; //水印 $scale = $_POST['scale']; //比例 800*600 $path = ''; //图片存储路径 //根据比例获取宽高 $width = substr($scale, 0, 3); //800 $height = substr($scale, 4, 3); //600 //上传图片并存储 require('UploadFile.php'); $upload = new UploadFile('test_pic'); $upload->setDestinationDir('./uploads'); $upload->setAllowMime(['image/jpeg', 'image/gif', 'image/png']); $upload->setAllowExt(['gif', 'jpeg', 'jpg', 'png']); $upload->setAllowSize(2 * 1024 * 1024); if ($upload->upload()) { $filename = $upload->getFileName()[0]; $dir = $upload->getDestinationDir(); $path = $dir . '/' . $filename; //图片存储的实际路径 } else { var_dump($upload->getErrors()); } //根据比例调整图像 require_once './lib/Image.php'; $image = new \test\Lib\Image($path); //放大并保存 $image->resize($width, $height)->save($path); $info = getimagesize($path); //根据不同的图像type 来创建图像 switch ($info[2]) { case 1: //IMAGETYPE_GIF $image = imagecreatefromgif($path); break; case IMAGETYPE_JPEG: $image = imagecreatefromjpeg($path); break; case 3: $image = imagecreatefrompng($path); break; default: echo '图像格式不支持'; break; } //添加水印 if ($mark == 1) { $logo = imagecreatefrompng('./uploads/logo.png'); //添加水印 imagecopy($image, $logo, 0, 0, 0, 0, imagesx($logo), imagesy($logo)); //header('Content-type:image/png'); imagejpeg($image, $path); } $dst_image = imagecreatetruecolor($width, $height); //拷贝源图像左上角起始 imagecopy($dst_image, $image, 0, 0, 0, 0, $width, $height); imagejpeg($dst_image, $path); //存入数据库 $servername = "localhost"; $username = "root"; $password = "123456"; $dbname = "pic"; try { $conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password); // 设置 PDO 错误模式,用于抛出异常 $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $sql = "INSERT INTO pic(`path`, `mark`, `scale`,`info`) VALUES ('$path', '$mark', '$scale','$description')"; // 使用 exec() ,没有结果返回 $conn->exec($sql); exit("<script>alert(\"图片上传成功!\");window.location=\"index.php\";</script>"); } catch (PDOException $e) { echo $sql . "<br>" . $e->getMessage(); }3、封装好的文件上传类:UploadFile.php <?php /** * Created by http://blog.yihang.info * User: 易航 * Date: 2022/7/03 * Time: 22:01 */ class UploadFile { const UPLOAD_ERROR = [ UPLOAD_ERR_INI_SIZE => '文件大小超出了php.ini当中的upload_max_filesize的值', UPLOAD_ERR_FORM_SIZE => '文件大小超出了MAX_FILE_SIZE的值', UPLOAD_ERR_PARTIAL => '文件只有部分被上传', UPLOAD_ERR_NO_FILE => '没有文件被上传', UPLOAD_ERR_NO_TMP_DIR => '找不到临时目录', UPLOAD_ERR_CANT_WRITE => '写入磁盘失败', UPLOAD_ERR_EXTENSION => '文件上传被扩展阻止', ]; /** * @var */ protected $field_name; /** * @var string */ protected $destination_dir; /** * @var array */ protected $allow_mime; /** * @var array */ protected $allow_ext; /** * @var */ protected $file_org_name; /** * @var */ protected $file_type; /** * @var */ protected $file_tmp_name; /** * @var */ protected $file_error; /** * @var */ protected $file_size; /** * @var array */ protected $errors; /** * @var */ protected $extension; /** * @var */ protected $file_new_name; /** * @var float|int */ protected $allow_size; /** * UploadFile constructor. * @param $keyName * @param string $destinationDir * @param array $allowMime * @param array $allowExt * @param float|int $allowSize */ public function __construct($keyName, $destinationDir = './uploads', $allowMime = ['image/jpeg', 'image/gif'], $allowExt = ['gif', 'jpeg'], $allowSize = 2 * 1024 * 1024) { $this->field_name = $keyName; $this->destination_dir = $destinationDir; $this->allow_mime = $allowMime; $this->allow_ext = $allowExt; $this->allow_size = $allowSize; } /** * @param $destinationDir */ public function setDestinationDir($destinationDir) { $this->destination_dir = $destinationDir; } /** * @param $allowMime */ public function setAllowMime($allowMime) { $this->allow_mime = $allowMime; } /** * @param $allowExt */ public function setAllowExt($allowExt) { $this->allow_ext = $allowExt; } /** * @param $allowSize */ public function setAllowSize($allowSize) { $this->allow_size = $allowSize; } /** * @return bool */ public function upload() { // 判断是否为多文件上传 $files = []; if (is_array($_FILES[$this->field_name]['name'])) { foreach ($_FILES[$this->field_name]['name'] as $k => $v) { $files[$k]['name'] = $v; $files[$k]['type'] = $_FILES[$this->field_name]['type'][$k]; $files[$k]['tmp_name'] = $_FILES[$this->field_name]['tmp_name'][$k]; $files[$k]['error'] = $_FILES[$this->field_name]['error'][$k]; $files[$k]['size'] = $_FILES[$this->field_name]['size'][$k]; } } else { $files[] = $_FILES[$this->field_name]; } foreach ($files as $key => $file) { // 接收$_FILES参数 $this->setFileInfo($key, $file); // 检查错误 $this->checkError($key); // 检查MIME类型 $this->checkMime($key); // 检查扩展名 $this->checkExt($key); // 检查文件大小 $this->checkSize($key); // 生成新的文件名称 $this->generateNewName($key); if (count((array)$this->getError($key)) > 0) { continue; } // 移动文件 $this->moveFile($key); } if (count((array)$this->errors) > 0) { return false; } return true; } /** * @return array */ public function getErrors() { return $this->errors; } /** * @param $key * @return mixed */ protected function getError($key) { return $this->errors[$key]; } protected function setFileInfo($key, $file) { // $_FILES name type temp_name error size $this->file_org_name[$key] = $file['name']; $this->file_type[$key] = $file['type']; $this->file_tmp_name[$key] = $file['tmp_name']; $this->file_error[$key] = $file['error']; $this->file_size[$key] = $file['size']; } /** * @param $key * @param $error */ protected function setError($key, $error) { $this->errors[$key][] = $error; } /** * @param $key * @return bool */ protected function checkError($key) { if ($this->file_error > UPLOAD_ERR_OK) { switch ($this->file_error) { case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: case UPLOAD_ERR_PARTIAL: case UPLOAD_ERR_NO_FILE: case UPLOAD_ERR_NO_TMP_DIR: case UPLOAD_ERR_CANT_WRITE: case UPLOAD_ERR_EXTENSION: $this->setError($key, self::UPLOAD_ERROR[$this->file_error]); return false; } } return true; } /** * @param $key * @return bool */ protected function checkMime($key) { if (!in_array($this->file_type[$key], $this->allow_mime)) { $this->setError($key, '文件类型' . $this->file_type[$key] . '不被允许!'); return false; } return true; } /** * @param $key * @return bool */ protected function checkExt($key) { $this->extension[$key] = pathinfo($this->file_org_name[$key], PATHINFO_EXTENSION); if (!in_array($this->extension[$key], $this->allow_ext)) { $this->setError($key, '文件扩展名' . $this->extension[$key] . '不被允许!'); return false; } return true; } /** * @return bool */ protected function checkSize($key) { if ($this->file_size[$key] > $this->allow_size) { $this->setError($key, '文件大小' . $this->file_size[$key] . '超出了限定大小' . $this->allow_size); return false; } return true; } /** * @param $key */ protected function generateNewName($key) { $this->file_new_name[$key] = uniqid() . '.' . $this->extension[$key]; } /** * @param $key * @return bool */ protected function moveFile($key) { if (!file_exists($this->destination_dir)) { mkdir($this->destination_dir, 0777, true); } $newName = rtrim($this->destination_dir, '/') . '/' . $this->file_new_name[$key]; if (is_uploaded_file($this->file_tmp_name[$key]) && move_uploaded_file($this->file_tmp_name[$key], $newName)) { return true; } $this->setError($key, '上传失败!'); return false; } /** * @return mixed */ public function getFileName() { return $this->file_new_name; } /** * @return string */ public function getDestinationDir() { return $this->destination_dir; } /** * @return mixed */ public function getExtension() { return $this->extension; } /** * @return mixed */ public function getFileSize() { return $this->file_size; } }4、搜索功能实现:search.php <?php // 接收请求数据 $pageNo = $_GET['page'] ?? 1; $pageSize = 9; // 接收查询参数 $keywords = $_GET['keywords'] ?? ''; $data = []; //模拟加载中的图标sleep(3); try { $pdo = new PDO( 'mysql:host=localhost:3306;dbname=pic', 'root', '123456', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION] ); // 请求mysql 查询记录总数 $sql = 'SELECT count(*) AS aggregate FROM pic'; if (strlen($keywords) > 0) { $sql .= ' WHERE info like ?'; } $stmt = $pdo->prepare($sql); if (strlen($keywords) > 0) { $stmt->bindValue(1, '%' . $keywords . '%', PDO::PARAM_STR); } $stmt->execute(); $total = $stmt->fetch(PDO::FETCH_ASSOC)['aggregate']; // 计算最大页码,设置页码边界 $minPage = 1; $maxPage = ceil($total / $pageSize); // 3.6 $pageNo = max($pageNo, $minPage); $pageNo = min($pageNo, $maxPage); $offset = ($pageNo - 1) * $pageSize; $sql = "SELECT `path`,`info` FROM pic "; if (strlen($keywords) > 0) { $sql .= ' WHERE info like ?'; } $sql .= 'ORDER BY id DESC LIMIT ?, ?'; $stmt = $pdo->prepare($sql); if (strlen($keywords) > 0) { $stmt->bindValue(1, '%' . $keywords . '%', PDO::PARAM_STR); $stmt->bindValue(2, (int)$offset, PDO::PARAM_INT); $stmt->bindValue(3, (int)$pageSize, PDO::PARAM_INT); } else { $stmt->bindValue(1, (int)$offset, PDO::PARAM_INT); $stmt->bindValue(2, (int)$pageSize, PDO::PARAM_INT); } $stmt->execute(); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); $data = [ 'code' => 1, 'msg' => 'ok', 'rows' => $results, 'total_records' => (int)$total, 'page_number' => (int)$pageNo, 'page_size' => (int)$pageSize, 'page_total' => (int)$maxPage, ]; } catch (PDOException $e) { $data = [ 'code' => 0, 'msg' => $e->getMessage(), 'rows' => [], 'total_records' => 0, 'page_number' => 0, 'page_size' => (int)$pageSize, 'page_total' => 0, ]; } header('Content-type: application/json'); echo json_encode($data);4、最后数据库格式 /* Navicat MySQL Data Transfer Source Server Version : 80012 Target Server Type : MYSQL Target Server Version : 80012 File Encoding : 65001 Date: 2020-01-14 16:22:49 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for pic -- ---------------------------- DROP TABLE IF EXISTS `pic`; CREATE TABLE `pic` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `path` varchar(255) DEFAULT NULL, `mark` tinyint(3) DEFAULT NULL, `scale` varchar(255) DEFAULT NULL, `info` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of pic -- ---------------------------- INSERT INTO `pic` VALUES ('1', './uploads/5e1d788084cc5.jpg', '1', '800*600', '这是测试图片1'); INSERT INTO `pic` VALUES ('2', './uploads/5e1d789766591.jpg', '1',);
-
JS唤醒Windows10/11消息通知 在写一个应用的时候需要显示网页来的消息,为了让用户不会错过消息,所以希望使用JS调用win10的通知消息,调用方法如下: JavaScript图片 JS调用window.Notification() 1、在页面打开的时候查看浏览器是否支持Notification API,如果支持,则判断是否有权限通知,没有的话交由用户判断是否允许通知(JS代码): // 判断浏览器是否支持唤醒 if (window.Notification) { let popNotice = () => { if (!Notification.permission === 'granted') return const notification = new Notification('阿巴阿巴', { body: '提示提示提示' }) // 点击通知的回调函数 notification.onclick = function() { window.open('https://baidu.com') notification.close() } } /* 授权过通知 */ if (Notification.permission === 'granted') { popNotice() } else { /* 未授权,先询问授权 */ Notification.requestPermission(function(permission) { popNotice() }) } }2.将应用部署到服务器之后只有https协议的网页可以调用通知功能。 如果是Win10/11系统的话,可以直接将上面代码复制到F12控制台运行