0%

查看expressAPI
对于文件下载,最简单的实现下载方法为如下

1
2
3
4
5
6
7
8
9
res.download('/report-12345.pdf', 'report.pdf', function(err){
if (err) {
// Handle error, but keep in mind the response may be partially-sent
// so check res.headersSent
} else {
// decrement a download credit, etc.
}
});

但是这种方法在实际使用中,发现了问题,那就是苹果safari浏览器在下载时候,文件标题会自动截取一段,或者乱码,或者问号,
一开始表示不解,IE都没事,利用fiddler进行抓包分析,发现res头部信息不对劲儿,存在两个filename,也就是不同浏览器对于重复filename处理,不一样,或者说safari对于重复filename会有问题,也就是res.download的写法毕竟是高度封装的,
换句话说,不要用这种高度封装的写法就好了,那么如何解决呢。
如下一种写法,这种没有高度封装,自己去写返回头部信息,经测试Safari下载果然没问题了。

1
2
3
4
5
6
7
8
let filename = "你好,地球人你好,地球人你好,地球人你好,地球人.pdf";
let filePath = path.resolve(__dirname, '..') + '/static/pdf/test.pdf';
let mimetype = mime.lookup(filePath);
res.setHeader('Content-Disposition', 'attachment; filename=' + new Buffer(filename).toString('binary'));
res.setHeader('Content-type', mimetype);
let filestream = fs.createReadStream(filePath);
filestream.pipe(res);

对比发现,写法1比写法2简单的多,但是目前对于Safari、IE支持是不好的,如果直接用写法2,Edge又会有问题,这时又要牵扯对于不同浏览器,文件名中英文一堆的逻辑判断处理,
所以最好的解决办法是根据请求头部,对应处理下。
对于浏览器判别可以用下面的类库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const parser = require('ua-parser-js');
let ua = parser(req.headers['user-agent']);
if (['Edge', 'Chrome', 'Firefox'].indexOf(ua.browser.name) > -1) {
res.download(filePath, filename, function (err) {
if (err) {
logger.error('有错误');
logger.error(err)
}
else {

}
}
);
}
else {
let mimetype = mime.lookup(filePath);
res.setHeader('Content-type', mimetype);
if (ua.browser.name == 'IE') {
res.setHeader('Content-Disposition', 'attachment; filename=' + encodeURIComponent(filename));
} /*else if (ua.browser.name == 'Firefox') {
res.setHeader('Content-Disposition', 'attachment; filename*="utf8\'\'' + encodeURIComponent(filename) + '"');
} */ else {
/* safari等其他非主流浏览器只能自求多福了 */
res.setHeader('Content-Disposition', 'attachment; filename=' + new Buffer(filename).toString('binary'));
}
let filestream = fs.createReadStream(filePath);
filestream.pipe(res);
}

浏览器支持

上述代码经过测试,支持以下浏览器

  • IE
  • Chrome
  • Firefox
  • Edge
  • 360(极速、兼容)
  • QQ(极速、兼容)

由于业务需要,进行了反编译的学习,有几点收获,这里总结下。

Android

什么是反编译

APP开发是将项目代码打包成一个APP,如果是安卓版就是个APK包,最终通过商店或者其它渠道,安装在用户的手机上。
而反编译是将APP包,进行逆向工程,最大程度的拿到原来的所有资源,如项目代码。

Android,iOS可行?

IOS本身是个封闭的环境,所以可行性几乎为0,没有大量的投入,这里你可以认为就是0
Android还具有一定的可行性,当然目前的APP一般都是做了代码混淆、安全加固的。

反编译流程

这里的反编译讲的是Android

环境准备

  • JAVA环境-建议1.8+
  • IDE,推荐Intelij IDEAAndroid Studio
  • Unix环境,推荐Mac,Ubuntu

反编译工具

玩了下在线工具decompileandroidjadxAndroidDecompiler,
对比推荐jadx,下面介绍下jadx基本使用

开始

下载ZIP包

网址:这里,下载最新版的zip包即可,注意不要选择源码。

环境变量配置

不同平台不一样,比如MAC,我是配置在cat ~/.bash_profile用户级环境变量配置,想立即生效,执行source ~/.bash_profile

运行GUI反编译工具

1
2
3
# 运行反编译GUI工具
$ jadx-gui

点击文件-打开文件,选择目标APK,等待工具自动执行,会看到工具将项目自动反编译成功,选择另存所有文件到某个地方即可。

利用IDE打开上一步另存出的项目CODE

如图为某瓣APP反编译出来的项目CODE

分析CODE

这点就看个人功力了,学过JAVA,学过Android会好很多

代码之外

除了反编译出来的项目代码外,还需要结合两点

玩APP

通过APP的具体操作,比如发帖按钮,其实对应去CODE中找对应的Activity等类库会好很多。

通过搭建模拟器,代理网络,抓包

除了APP本身,APP是需要与服务端进行交互的,所以利用Fiddler等抓包工具,进行抓包分析,可以拿到接口信息,从而印证某些推断。
目前的HTTP请求很多都是加密了的,即HTTPS,这个需要证书来解决了,建议查下相关资料吧。

写在最后

— 目前安卓开发,在打包时候都是会进行代码混淆的,所以即使去壳反编译后,也不是说就完全的展示了项目的情况,但是基本上并不影响大体的阅读。
其实反编译一些优秀的app,阅读别人的代码,了解别人的整体设计,是种很不错的学习形式。

  • 前端本身是透明的,所以反编译用来学习很好,不要直接盗窃别人的劳动成果,这点是需要注意的。

不知不觉,一年过去,回顾2016,蓦然发现,全年度几乎在做一个项目,一个项目复杂,难度,庞大,大到我需要付出大半年的时间去学习,去开发。与其说是项目的艰巨,不如说是自己的太差,时刻面临着挑战,这种感觉用战战兢兢,一点不为过,性格使然,家风使然,我不喜欢糊涂这么个词,于是我不断的打磨,打磨到客户都觉得慢了,中间,也做了数次的妥协,但今时今日再看自己付出心血参与的项目,内心的感觉,有安心,开心,也有眼泪,更有说不出的感慨。当项目日臻成熟,跃跃上线时,我少了那份焦躁,磨练出的是镇定的看待他人反馈的任何问题。

时间,打磨一个事儿,更修炼一个人。

全年的日夜奋战,拖垮了身体的大半(.1的夸张)但但凡的付出也总是换来不菲的回报,如今再看任何的技术,所谓的牛,我也不再是以前的那般感觉,我更相信掌握技术需要的是扎实的内功,敏捷的思维,足够的勤奋,及对所做的事,影响的人乃至自己有足够多的尊重,而已。

于今年,我感谢指导我的人,支持我的人,关心我的人。

于未来的一年,我更希望平衡工作与生活,实现良性的节奏感。

留一文,纪念这多磨多难的2016。

【分享】人都需要具备阶段性的自醒、外醒及觉知的能力:看待事物不是东不是西,而是指向内心,通过时时刻刻地自我批判性思考,衡量周围人事物不断变化对三观的影响,从而觉知下一阶段的自己。对大多数正确的废话置放层面取决于心智成熟度,人生没有对错的概念,只是角度问题。

HTTP是无状态协议,维持前后端的用户状态,Session是一种方案,Express下如何去做呢,看下文

  1. 安装Session中间件
    npm i express-session --save

2.Session配置开启
app.js下进行如下配置,这里直接贴出完整文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const express = require('express');
const app = express();
const conf = require('./config');
const routes = require('./routes/index');
const path = require('path');
const bodyParser = require('body-parser');
const isDeveloping = (process.env.NODE_ENV || 'development') == 'development';
const session = require("express-session");
app.enable('trust proxy'); // trust first proxy
app.use(bodyParser.json()); // for parsing application/json
const sessionConfig = {
secret: "Shh, its a secret!",
resave: false,
saveUninitialized: true
};
if (!isDeveloping) {
const RedisStore = require('connect-redis')(session);
sessionConfig.store = new RedisStore(conf.redis);
}
app.use(session(sessionConfig));
// mount the router on the app
app.use('/', routes);
//配置静态资源
app.use('/', express.static(path.join(__dirname, '/static')));

if (!isDeveloping) {
app.use('/', express.static(path.join(__dirname, 'dist')));
app.get('*', function (req, res) {
res.sendFile(__dirname + '/dist/index.html');
});
}

app.listen(conf.server.port, "127.0.0.1", function () {
console.log(`campus-server app listening on port ${conf.server.port}!`);
}
);
  1. 用户登录更新Session
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
router.post('/login', (req, res) => {
const user = appUsers[req.body.email];
if (user && user.password === req.body.password) {
const userWithoutPassword = {...user};
delete userWithoutPassword.password;
req.session.user = userWithoutPassword;
res.status(200).send({
user: userWithoutPassword
});
} else {
res.status(403).send({
errorMessage: 'Permission denied!'
});
}
}
);

  1. 用户退出 销毁Session
1
2
3
4
5
6
7
8
9
router.get('/logout', function (req, res) {
req.session.destroy((err) => {
if (err) {
res.status(500).send('Could not log out.');
} else {
res.status(200).send({});
}
});
});
  1. 改变Session存储方案
    Session存储的默认存储方案为MemoryStore,当我们生产环境应用时,会得到如下提示
    1
    2
    Warning: connect.session() MemoryStore is not
    designed for a production environment, as it will leak
    比如这里,我使用Redis作为生产级存储方案,所以服务器需要安装Redis,yum install -y redis
    同时,后端需要安装对应中间件npm install connect-redis --save,所以再看上面的配置,就明白为什么生产环境下,要加Redis配置喽。

点击ss,选择编辑用户规则
格式如下

1
2
3
4
! Put user rules line by line in this file.
! See https://adblockplus.org/en/filter-cheatsheet
||amazonaws.com
||atom.io

保存即可。

规则大概描述如下
1
2
3
4
5
6
7
8
9
通配符支持,如 *.example.com/* 实际书写时可省略 * 如 .example.com/ 意即 *.example.com/*
正则表达式支持,以\开始和结束, 如 \[\w]+:\/\/example.com\
例外规则 @@,如 @@*.example.com/* 满足@@后规则的地址不使用代理
匹配地址开始和结尾 |,如 |http://example.com、example.com| 分别表示以 http://example.com 开始
和以 example.com 结束的地址|| 标记,如 ||example.com 则 http://example.com 、https://example.com 、ftp://example.com 等地址均满足条件,只用于匹配地址
开头
注释 ! 如 ! Comment
分隔符^,表示除了字母、数字或者 _ - . % 之外的任何字符。如 http://example.com^ ,http://example.com/ 和
http://example.com:8000/ 均满足条件,而 http://example.com.ar/ 不满足条件

搬瓦工简介
搬瓦工(BandwagonHost)是美国IT7公司旗下的一家提供便宜年付OVZ架构的VPS主机方案的服务商。
因其价格便宜、且依托的商家比较靠谱,具有较高的性价比。
https://bandwagonhost.com/