2016年7月

占位图片的优化

背景

在前端开发切图的时候,很多时候示例图片都是不需要的,只需要一个尺寸的图片,还方便后端清晰的知道要输出的图片尺寸。

对于访问时候的优化,有2个方案:

  1. 使用cdn回源 (对于对外开放的服务,自然会在cdn上留下大量一次性的图片)
  2. 在服务器的内存中生成图片返回 (由于只是提供开发者使用,只要对http缓存做好优化,压力不会很大)

大家都知道对于静态图片,nginx对于缓存这些东西都给我们做好了,只需要1~2行的配置就可以搞定;但是注意了,我们这里是在程序中动态生成图片,所以我们需要关心http中浏览器的缓存头

Etag

Expires:
Cache-Control: max-age=3600
Last-Modified:
If-Modified-Since:
ETag:
If-None-Match:

Cache-Control

http-cache-decision-tree.png

这里我采用的是对 text, fg, bg, width, height 进行md5处理,因为这几个值就可以确定唯一的图片

$md5 = md5(json_encode(compact('width', 'height', 'text', 'bg', 'fg')));
$etags = Input::getEtags();
if (isset($etags[0])) {
    $etag = str_replace('"', '', $etags[0]);
    if ($etag === $md5) {
        App::abort(304, '', ['Content-Type' => 'image/png', 'Cache-Control' => 'public, max-age=31536000', 'ETag' => $md5]);
    }
}

YUV 图片文字颜色的获取 (颜色明亮度)

更多的使用的时候,往往我们只需要指定背景色,不关心文字的颜色(能看清就行);于是在不同的背景色下面,我们都需要让文字颜色和背景色保持一定的对比度,好了直接看代码吧

$greyLevel = $r * .229 + $g * .587 + $b * .114;
if ($greyLevel >= 189) { // 192
    $fg = '#666666';
} else {
    $fg = '#ECF0F1';
}

字体选择的优化

全英文的情况下 Georgia 的展现效果比较不错,但是包含中文之后效果就大打折扣,所以这里有个小兼容

$fontFamily = 'Georgia.ttf';
// 如果包含中文
if (preg_match('/[\x{4e00}-\x{9fa5}]/u', $text)) $fontFamily = 'yahei_mono.ttf';

安全控制

由于图片的生成会消耗一定的内存,而且图片尺寸越大,耗的内存越多,所以这里对生成图片的尺寸做了1个限制,就是高宽都必须小于等于 2048,否则返回 404

搭建私有播放服务

背景

由于很多时候,视频资源下载很慢,需要用自己的电脑挂机;又或是国外的资源被墙,无法观看;所以需要一个可以挂机下载的服务器。

离线下载这步做到之后,又由于download到自己电脑上还需要一段时间,而且有些资源可能是不需要的, 所以又有了在线观看的需求。

使用到的软件/框架

  1. Scrapy
  2. MySQL
  3. Aria2
  4. Ffmpeg
  5. Nginx
  6. Laravel
  7. Videojs

流程图

下载流程

downloader.png

展示流程

player.png

痛点

其实正常的开发流程一般遵照网上的教程或者官方文档就可以很容易的解决;这边讲讲一些需要费一些精力才能解决的问题

被js加密过的下载地址

一般都是使用eval来执行加密过的字符串,只要先将eval里面的参数给解密出来,然后使用正则匹配解密之后的字符串里面的下载地址就可以了

p = re.compile(ur'eval\((.*)')
m = p.search(response.body)
if not m:
    print('---- not match ----')
    return
jsctx = PyV8.JSContext(PyV8.JSClass())
jsctx.enter()
jsctx.eval(r"var a = new String(" + m.group(1))
print(jsctx.locals.a)
content = jsctx.locals.a

p1 = re.compile('(http://[^"]+)')
m1 = p1.search(content)

if not m1:
    print('---- cannot find url in js content ----')
    return

videoUrl = m1.group(1)

python和aria2的交互

# encoding: utf-8
import xmlrpclib

class Pipeline(object):
    def __init__(self):
        self.s = xmlrpclib.ServerProxy('http://localhost:6800/rpc')

    def init_hook(self):
        self.s.aria2.onDownloadStart(self.onDownloadStart)
        self.s.aria2.onDownloadError(self.onDownloadError)
        self.s.aria2.onDownloadComplete(self.onDownloadComplete)

    def onDownloadStart(self, event):
        pass

    def onDownloadError(self, event):
        pass

    def onDownloadComplete(self, event):
        pass

    def process_item(self, item, spider):
        # ...
        gid = self.s.aria2.addUri([item['video_url']], {"out": path})
        # ...

ffmpeg将mp4拆分为HLS

命令如下,注意替换<>之间的内容

ffmpeg -i <ccc.mp4> -vf scale=-2:360 -profile:v baseline -level 3.0 -start_number 0 -hls_time 10 -hls_list_size 0 -f hls <ccc.m3u8>

ffmpeg截取部分视频为gif

注意替换命令中的两个变量

ffmpeg -ss 00:00:10 -t 5 -i "$filename" -vf scale=360:-1 -b 2048k "$dirname".gif

videojs播放HLS

默认情况之下videojs是不支持播放HLS的,需要引入 videojs-contrib-hls.min.js

videojs.options.flash.swf = "/js/video-js/video-js.swf";
videojs('main_video').ready(function() {
  this.hotkeys({
    volumeStep: 0.1,
    seekStep: 5,
    enableMute: true,
    enableFullscreen: true,
    enableNumbers: true
  });
});

媒体中心设计分享

更详细的内容可以查看文章结尾的PDF

媒体文件处理

magic bytes 校验
扩展名校验
文件名校验
mime校验
速率限制

图片的处理

  1. (水印颜色的优化) 颜色深浅的判断
  2. 图片中心的判断
  3. 颜色色差的聚类

音频的处理

视频的处理

http api跨域

iframe

同源策略,document.domain = tool.lu

jsonp

这种跨域实际上是在页面中动态插入一个 <script> 标签,然后在被加载的脚本中执行当前网页里的函数;显而易见,只支持GET请求。

ajax2

HTTP access control (CORS) - Cross-Origin Resource Sharing

  1. 简单请求
  2. 预检请求

http返回头

Access-Control-Allow-Origin: <origin> | *
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
Access-Control-Max-Age: <delta-seconds>
Access-Control-Allow-Credentials: true | false
Access-Control-Allow-Methods: <method>[, <method>]*
Access-Control-Allow-Headers: <field-name>[, <field-name>]*
static const auto maxPreflightCacheTimeout = std::chrono::seconds(600);  

http请求头

Origin: <origin>
Access-Control-Request-Method: <method>
Access-Control-Request-Headers: <field-name>[, <field-name>]*

默认情况下 跨域XHR 是不会带上Cookie的,需要设置 withCredentials = true;

preflight.png
request.png

附件:mediacenter.pdf

使用phpexcel解析Excel

使用composer管理

composer require phpoffice/phpexcel
<?php

error_reporting(E_ALL);
date_default_timezone_set('Asia/Shanghai');

require __dir__ . '/../vendor/autoload.php';

// ... 注意自己赋值$file
$excel = PHPExcel_IOFactory::load($file);
$sheetCount = $excel->getSheetCount();
echo "sheet count:$sheetCount" . PHP_EOL;
foreach ($excel->getWorksheetIterator() as $sheet) {
    if ($sheet->getSheetState() === 'hidden') {
        continue;
    }
    $sheetName = $sheet->getTitle();
    echo "sheet name: $sheetName" . PHP_EOL;
    $highestColumn = $sheet->getHighestColumn();
    $highestColumnIndex = PHPExcel_Cell::columnIndexFromString($highestColumn);
    $highestRow = $sheet->getHighestRow();
    for ($row = 1; $row <= $highestRow; $row++) {
        $columns = [];
        for($col = 0; $col < $highestColumnIndex; $col++) {
            $cell = $sheet->getCellByColumnAndRow($col, $row);
            $val = $cell->getValue();
            if ($val instanceof PHPExcel_RichText) {
                $columns[] = trim($val->getPlainText());
            } else {
                $columns[] = trim($val);
            }
        }
        var_dump($columns);
    }
}

过滤隐藏的worksheet

if ($sheet->getSheetState() === 'hidden')

过滤隐藏的行

由Excel中auto filter隐藏的行

if ($sheet->getRowDimension($row)->getVisible())

获取单元格颜色

获取文字颜色

$cell->getStyle()->getFont()->getColor()->getRGB();

获取填充颜色

$cell->getStyle()->getFill()->getStartColor()->getRGB();

颜色的差异值

是这样,运营那边对不同类型的数据进行了颜色的分类,但是是多个人整理的,所以紫色淡紫色的运营那边认为是同一个东西;但是程序里面是没法判断,因为紫色的区域太大了;所以这边就需要用到颜色的差异值比对了。

| YUV |      -      |   -   |
|-----|-------------|-------|
|  Y  |     Luma    | 明亮度 |
|  U  | Chrominance |  色度  |
|  V  |    Chroma   |  浓度  |

Y'UV 的发明是由于彩色电视与黑白电视的过渡时期。黑白视讯只有 Y(Luma,Luminance)视讯,也就是灰阶值。到了彩色电视规格的制定,是以 YUV/YIQ 的格式来处理彩色电视图像,把 UV 视作表示彩度的 C(Chrominance或Chroma),如果忽略 C 讯号,那么剩下的 Y(Luma)讯号就跟之前的黑白电视讯号相同,这样一来便解决彩色电视机与黑白电视机的相容问题。

|  Lab  |  -   |   -   |
|-------|------|-------|
|   L   | Luma | 明亮度 |
|   a   |   a  |   -   |
|   b   |   b  |   -   |

RGB -> XYZ -> Lab -> delta E

RGB to closest predefined color
color distance
RGB到Lab的转换

| RGB |   -   |  -  |
|-----|-------|-----|
|  R  |  Red  | 红色 |
|  G  | Green | 绿色 |
|  B  | Blue  | 蓝色 |

php默认函数 imagecolorclosest // RGB几何距离,由于RGB空间不是颜色感知连续的,所以效果没有Lab'的好

这边有个实现好的颜色空间转换的代码 hasbridge/php-color