平常开发过程当中,咱们常常须要修改一些放在 CDN 上的静态文件(如 JavaScript、CSS、HTML 文件等),这个过程当中,咱们但愿能有一种方式将线上 CDN 的目录映射为本地硬盘上的某个目录,这样,当咱们在本地修改了某个文件时,不须要发布,刷新后立刻能看到效果。css
好比,咱们的 CDN 域名是:http://a.mycdn.com,本地对应的目录是:D:\work\assets,咱们但愿全部对 http://a.mycdn.com/* 的访问被映射到本地的 D:\work\assets\* 下。如访问 http://a.mycdn.com/s/atp.js 时,其实是读取的是本地的 D:\work\assets\s\atp.js,而不须要从网上下载线上的文件。node
实现这个功能很简单,关键点以下:服务器
一、在本地开启一个 HTTP 服务,监听 80 端口;
二、修改系统 hosts 文件,添加“127.0.0.1 a.mycdn.com”,将 CDN 域名绑定为本地服务器地址;
三、配置本地 HTTP 服务,接收到一个 GET 请求后,先检查本地硬盘上是否存在对应的文件,如存在,则返回这个文件的内容,如不存在,则返回线上对应的内容。并发
能够看到,关键部分是须要搭建一个本地的 HTTP 服务。这方面有不少教程,好比在本地安装 Apache 或 Ngnix 等服务器软件,再配置相应的转发规则等。不过我的以为这类方法仍是有点复杂,本文要介绍的,是另外的不须要安装服务器软件的方法。性能
由于咱们是在本地开发调试,对性能、并发性的要求并不高,所以咱们其实并不须要一个像 Apache/Ngnix 这样的专业的 HTTP 软件,咱们只须要一段能提供 HTTP 服务的脚本便可。好比用 nodejs 来实现。ui
01 |
/** |
02 |
* author: oldj |
03 |
* blog: http://oldj.net |
04 |
* |
05 |
**/ |
06 |
07 |
var http = require( "http" ), |
08 |
url = require( "url" ), |
09 |
path = require( "path" ), |
10 |
fs = require( "fs" ), |
11 |
local_folders, |
12 |
base_url; |
13 |
14 |
local_folders = [ // 本地路径,代理将在这个列表中的目录下寻找文件,若是没有找到则转到线上地址 |
15 |
"D:/work/assets" |
16 |
]; |
17 |
base_url = "http://10.232.133.214" ; // 线上路径,若是找不到文件,则转向到这个地址 |
18 |
19 |
20 |
function loadFile(pathname, response) { |
21 |
var i, l = local_folders.length, |
22 |
fn; |
23 |
24 |
console.log( "try to load " + pathname); |
25 |
26 |
for (i = 0; i < l; i++) { |
27 |
28 |
fn = local_folders[i] + pathname; |
29 |
if (path.existsSync(fn) && fs.statSync(fn).isFile()) { |
30 |
fs.readFile(fn, function (err, data) { |
31 |
response.writeHead(200); |
32 |
response.write(data); |
33 |
response.end(); |
34 |
}); |
35 |
36 |
return ; |
37 |
} |
38 |
39 |
} |
40 |
41 |
response.writeHead(302, { |
42 |
"Location" :base_url + pathname |
43 |
}); |
44 |
response.end(); |
45 |
} |
46 |
47 |
http.createServer( |
48 |
function (request, response) { |
49 |
50 |
var req_url = request.url, |
51 |
pathname; |
52 |
53 |
// 处理相似 http://a.tbcdn.cn/??p/global/1.0/global-min.css,tbsp/tbsp.css?t=20110920172000.css 的请求 |
54 |
pathname = req_url.indexOf( "??" ) == -1 ? url.parse(request.url).pathname : req_url; |
55 |
console.log( "Request for '" + pathname + "' received." ); |
56 |
loadFile(pathname, response); |
57 |
58 |
}).listen(80); |
注意将上面的 local_folders 和 base_url 两个变量的值修改成你须要的值。将这个文件保存下来,好比保存为 local-cdn-proxy.js,而后在命令行里执行“node local-cdn-proxy.js”,本地服务器就运行起来了,固然,别忘了绑定 hosts 。url
当经过 http 访问一个路径时,上面的脚本会先在本地对应的目录下查找,找到则返回对应文件的内容,找不到则直接 302 跳转到线上对应的地址。对于找不到的状况,还有一种处理办法是由本地服务器从线上下载对应的内容并返回,不过对这个需求来讲,302 跳转就足够了。.net
除了 nodejs 版本,我也写了一个 Python 的版本:命令行
01 |
# -*- coding: utf-8 -*- |
02 |
# |
03 |
# author: oldj |
04 |
# blog: http://oldj.net |
05 |
# |
06 |
07 |
import os |
08 |
import BaseHTTPServer |
09 |
10 |
LOCAL_FOLDERS = [ |
11 |
"D:/work/assets" |
12 |
] |
13 |
BASE_URL = "http://10.232.133.214" |
14 |
15 |
class WebRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): |
16 |
17 |
def do_GET( self ): |
18 |
print "Request for '%s' received." % self .path |
19 |
for folder in LOCAL_FOLDERS: |
20 |
fn = os.path.join(folder, self .path.replace( "/" , os.sep)[ 1 :]) |
21 |
if os.path.isfile(fn): |
22 |
self .send_response( 200 ) |
23 |
self .wfile.write( open (fn, "rb" ).read()) |
24 |
break |
25 |
26 |
else : |
27 |
self .send_response( 302 ) |
28 |
self .send_header( "Location" , "%s%s" % (BASE_URL, self .path)) |
29 |
30 |
server = BaseHTTPServer.HTTPServer(( "0.0.0.0" , 80 ), WebRequestHandler) |
31 |
server.serve_forever() |
能够看到,Python 版本的代码比 nodejs 版本的精简了不少。代理
上面的两段代码的功能还相对比较简单,好比没有输出内容的 MIME-Type、Content-Length 等头信息,对可能的阻塞操做(如读取文件超时等)也没有作特别的处理。对于本地开发环境来讲,它们已是能够工做的版本了,你也能够继续扩展这两个脚本,以便知足更多的需求。