All Posts

如何做一个完美的页码跳转

需求 想给系统实现一个选择不同页面的功能,一开始的代码逻辑比较混乱,后来抽象出来就比较清楚了。第一步,咱们先说需求: 问题定义 我们希望实现一个页面切换,每次显示的可选的页码长度都是固定的,比如从第 1 页到第 11 页,从 21 页 到 31 页。这样能够实现一个统一的切换效果,可能还需要考虑一些边界情况。现在,我们令总页码数为 pages,当前选择的页码为 p,p 往左走或者往右走的步长是固定的,令步长为 step。那么我们现在要做的事情可以这么理解,我们要从 1 到 pages 之间截取可用的页码数,假设开始页码为 startIndex,结束页码为 endIndex。抽象一下,我们可以总结出以下几种情况: Condition1 startIndex < 1 && endIndex <= pages Condition2 startIndex >= 1 && endIndex > pages Condition3 startIndex < 1 && endIndex > pages Condition4 startIndex >= 1 && endIndex <= pages 这样抽象成四种情况,这样就比较容易理解。以线段的方式来理解,则是从 1 到 pages 截取页码。 代码实现 Show me the code. func GetPageList(p, step, pages int) ([]int) { pageList := make([]int, 0) startIndex := p - step endIndex := p + step if startIndex < 1 && endIndex <= pages { startIndex = 1 endIndex = startIndex + 2 * step } else if startIndex >= 1 && endIndex > pages { endIndex = pages startIndex = pages - 2 * step } else if startIndex < 1 && endIndex > pages { startIndex = 1 endIndex = pages } // handle some special cases if startIndex < 1 { startIndex = 1 } if endIndex > pages { endIndex = pages } for i := startIndex; i <= endIndex; i++ { pageList = append(pageList, i) } return pageList } 结语 没有思考清楚的时候,你的逻辑是混乱的,写出来的代码也是混乱的。所以先整理好思路,想好应该怎么写,可以画画图,理理思路,这样写出的代码既有逻辑出现 bug 的概率也会大大降低。另外一点,很多人觉得写业务和算法可能就相去甚远,都有时候认真想想,或许你的业务代码也可以抽象成一个小算法。

pwa, 上海地铁线路图全新重构

之前一直有在维护一个上海地铁线路图的 pwa,最主要的特性就是 “offline first”。但是由于代码都是通过原生的 js 去实现,之前我都不是很喜欢去用框架,不想具有任何框架的偏好。但是到后期随着代码量的增加,代码的确变得混乱不堪,拓展新功能也变得尤为困难。因此,花了将近两个礼拜的时候对于应用进行了一次完整的重构。网站访问地址:https://neal1991.github.io/subway-shanghai 准备 准备工作先做好,在 vue 和 react 之间,我还是选择了后者。基于 create-react-app 来搭建环境,crp 为你准备了一个开箱即用的开发环境,因此你无需自己亲手配置 webpack,因此你也不需要成为一名 webpack 配置工程师了。 另外一方面,我们还需要一些数据,包括站点信息,线路路径,文字说明等等。基于之前的应用,可以通过一小段的代码获取信息。就此如要我们获取我们以前的站点在 svg 图中的相关属性,普通的站点使用 circle 元素,为了获取其属性: const circles = document.querySelectorAll('circle'); let result = []; circles.forEach(circle => { let ele = { cx: circle.cx, cy: circle.cy, sroke: circle.stroke, id: circle.id }; result.push(ele); }) const str = JSON.stringify(result); 通过这样的代码我们就可以获取 svg 普通站点信息,同理还可获取中转站信息,线路路径信息以及站点以及线路 label 信息。还有,我们还需要获取每个站点的时刻表信息,卫生间位置信息,无障碍电梯信息以及出入口信息。这里是写了一些爬虫去官网爬取并做了一些数据处理,再次就不一一赘述。 设计 数据准备好之后,就是应用的设计了。首先,对组件进行一次拆分: 组件结构 将整个地图理解成一个 Map 组件,再将其分为 4 个小组件: Label: 地图上的文本信息,包括地铁站名,线路名称 Station: 地铁站点,包括普通站点和中转站点 Line: 地铁线路 InfoCard: 状态最复杂的一个组件,主要包含时刻表信息、卫生间位置信息、出入口信息、无障碍电梯信息 这是一个大致的组件划分,里面可能包含更多的其它元素,比如 InfoCard 就有 InfoCard => TimeSheet => TimesheetTable 这样的嵌套。

通过七牛云建立私有图床

七牛云是国内一家领先的云存储公司,可以利用七牛云存储对象存储图片。虽然现在各种图床,但还是希望能够搭建一个私有的图床。所以一直有希望使用七牛云搭建图床的想法,之前一直没有好好地看懂七牛云的 SDK,后来在仔细地看了一遍之后,终于知道如何利用官方的 SDK 来实现图片上传。过年在家花了一点时间,后来陆续也写了一点,完成了这个七牛云图床 chrome 拓展。 注册账户 首先你可以通过这个链接注册你的七牛云账户。在成功注册账户之后,可能还需要绑定手机号,你就可以创建存储空间,可以理解成为文件存储的文件夹。 创建好存储空间(bucket)就已经完成了私有图库的第一步。 开发 在这也会对所有代码一一解释,主要是讲解一下在开发中遇到的一些问题。首先基于七牛云存储开发,有必要学会七牛云存储 API 的使用。可以在官方 SDK 文档获取所有文档。本拓展的开发主要是基于 js 来进行开发,因此我们只需要了解 js SDK 文档。 文档中提到了一点:JS-SDK 依赖服务端颁发 token,可以通过以下二种方式实现: 利用七牛服务端 SDK 构建后端服务 利用七牛底层 API 构建服务,详见七牛上传策略和上传凭证(https://developer.qiniu.com/kodo/manual/1208/upload-token) 第一个方法还需要搭建服务器来颁发 token,显然这种方法不太经济,如果仅仅是为了这个图床搭建一个后端服务,就不太划算了。因此,我选择第二种,在客户端来生成 token。这种方法就需要你了解上传策略以及上传凭证。 上传策略是资源上传时附带的一组配置设定。通过这组配置信息,七牛云存储可以了解用户上传的需求:它将上传什么资源,上传到哪个空间,上传结果是回调通知还是使用重定向跳转,是否需要设置反馈信息的内容,以及授权上传的截止时间等等。上传策略主要是 scope 和 dealine 这两个字段是必须要的。scope 是指定上传的目标资源空间 Bucket 和资源键 Key,这里我们只需要设置 bucket。deadline 是上传凭证有效截止时间。Unix时间戳,单位为秒。该截止时间为上传完成后,在七牛空间生成文件的校验时间,而非上传的开始时间,官方建议建议设置为上传开始时间 + 3600s。 function genPolicy(scope) { let policy = { scope: scope, deadline: (new Date()).getTime() + 3600 } return policy; } 按照上述算法流程构建客户端的上传 token,官方有提供上传凭证的在线示例,通过整理形成了自己的 token.js。

聊聊答题应用题库的建立

前段时间,答题 APP 如火如荼的发展,各大互联网公司都加入了撒币大战,包括像冲顶大会,百万英雄,芝士英雄等等。随之而来的也是各个答题应用辅助的兴起。 网上已经有不少答题应用的辅助,一般来说包括两个步骤,即获取题目选项以及搜索答案。对于题目以及选项的获取包括利用 adb 抓取手机屏幕截图,然后使用 ocr(optical character recognization) 的方式去识别题目和选项。大多数使用的 ocr 工具有谷歌开源的 tesseract-ocr以及百度的 ocr API。谷歌的 tesseract-ocr 可以在本地进行安装,软件下载地址是 https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-setup-3.05.01.exe , 安装的时候注意选择增加中文简体语言包,否则无法识别中文。另外一种方法就是利用百度的 ocr API,可以免费申请,使用起来比较方便,识别率相对来说也更加准确。百度 API 还有一个优点是图片无需处理就可以进行识别,而 tesseract-ocr 一般还需要对图片进行简单的处理。获取题目以及选项的另外一种方式就是使用抓包工具去抓取 APP 请求从而获取题目以及选项信息。 另一方面,对于题目答案的搜索。常见的几种做法是直接用题目作为搜索关键字打开浏览器,或者是问题加选项搜索,获取搜索引擎搜索的结果数量。通过结果数量来判断问题和选项的相关性从而判断问题的答案,一般来说这种方式获取的答案都是不太准确的,一是因为现在题目的出题方式越来越诡异,二是相关性越大并不一定就意味着是正确答案。本来对于题目和选项的判断就是很难的一件事情,除非你能做出很完美的语意理解,否则很难判断出正确的选项。还有一种比较直白的方式就是建立题库。在本文中,我们讨论一种建立题库的方式,这里只是做一个简单的探索,未必在实际中就能够使用,因为题库必须足够全才能够发挥威力。 使用 elasticsearch 建立题库 本文主要讲解关于题库的建立方面的很小的一方面进行探索,对于答题辅助的使用可以阅读原文查看完整介绍,代码主要是基于TopSup 做了一些调整。Elasticsearch 将被用于题库的建立,对于 es 的安装可以查看第一篇文章。有人可能会觉得用 es 来做题库,简直就是高射炮打蚊子——小题大做。但我觉得 es 安装和使用都很方便,得益于其强大的 RESTFUL接口,几乎可以用任何工具操控 es。Talk is cheap, show me the code. from elasticsearch import Elasticsearch def write_quetion(): question = { 'question': '谁是世界上最帅的人', 'answer': 'Neal' } es = Elasticsearch({'localhost'}) es.index(index='question-index', doc_type='question', id=1, body=question) 上面是一个简单的像索引中写入一条记录的代码片段,其实 es 可以算是一种非关系型数据库,在 DB-Engines 的最新排名中,es 已经蹿到了第 9 名。Elasticsearch 中的某些概念可以和关系型数据库进行类比:

POI读取文件的最佳实践

POI是 Apache 旗下一款读写微软家文档声名显赫的类库。应该很多人在做报表的导出,或者创建 word 文档以及读取之类的都是用过 POI。POI 也的确对于这些操作带来很大的便利性。我最近做的一个工具就是读取计算机中的 word 以及 excel 文件。下面我就两方面讲解以下遇到的一些坑: word 篇 对于 word 文件,我需要的就是提取文件中正文的文字。所以可以创建一个方法来读取 doc 或者 docx 文件: private static String readDoc(String filePath, InputStream is) { String text= ""; try { if (filePath.endsWith("doc")) { WordExtractor ex = new WordExtractor(is); text = ex.getText(); ex.close(); is.close(); } else if(filePath.endsWith("docx")) { XWPFDocument doc = new XWPFDocument(is); XWPFWordExtractor extractor = new XWPFWordExtractor(doc); text = extractor.getText(); extractor.close(); is.close(); } } catch (Exception e) { logger.

基于ELK进行邮箱访问日志的分析

公司希望能够搭建自己的日志分析系统。现在基于ELK的技术分析日志的公司越来越多,在此也记录一下我利用ELK搭建的日志分析系统。 系统搭建 系统主要是基于elasticsearch+logstash+filebeat+kibana+nginx,其实我这个用的还是比较多的,可以直接用logstash直接去采集日志。不过由于logstash的性能影响都比较大,而且filebeat安装很方便,而且占用资源很小,所以现在filebeat现在被广泛应用于日志采集。 其实在搭这个系统还是比较麻烦的,可是前面有的踩过的坑当时没有及时记录下来,有点忘记了。但是里面就是配置logstash和filebeat配置证书的时候有点麻烦,配置不好会一直没有办法连通。还要注意ES的索引占得空间,其实ES索引还蛮占空间的。 Logstash Logstash其实在整个ELK中环节还蛮重要的,其实可以理解为一个“中间人”的角色。它通过从filebeat中接受数据,然后进行过滤,最后再传输给es。所以一般logstash的配置也包括input,output以及filter的配置。 filter logstash中的filter比较重要,可以对日志利用正则进行过滤,这样你可以更关心日志中你需要关注的字段。强烈建议去grokdebugger去调试你的grok正则表达式,但是国内访问速度比较慢,可以采取一定手段访问。上面还有grok内置的一些常用正则表达式,可以配合试用调试。 geoip 日志分析中往往涉及到ip归属地的查询。logstash自带的geoip插件已经自带了数据库,可以下载最新的数据库。同时,geoip里面包含了很多信息,你可以进行过滤,只选择自己想要的字段: geoip { fields => ["city_name", "country_name"] } 日志分析 邮箱日志的格式是IIS的日至格式,日志是由空格分割开的一些字段信息。主要的字段包含以下这些字段信息: #Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) sc-status sc-substatus sc-win32-status time-taken 针对这个日志,我利用grok去解析这些字段的信息,自定义的正则规则是: DATE_CH \d+[/-]\d+[/-]\d+ OUTER_EMAIL %{DATE_CH:date} %{TIME:time} %{IP:serverIp} %{WORD:method} %{URIPATH:uristem} %{PARAM:query} %{INT:port} %{NOTSPACE:username} %{IP:clientIp} %{NOTSPACE:ua} %{INT:status} %{INT:substatus} %{INT:win32status} %{INT:timetaken} 通过grok我们可以获取这些字段,但如何在这些字段中挖掘有用的信息呢?这里面比较有价值的信息就是用户的登录时间,登录客户端,以及登录的ip。通过之前的 geoip 的配置,我们可以获取到ip对应的地址信息。登录时间由于很多邮件客户端在后台会去同步或者去登陆,所以参考意义不是特别的大。 后续对于日志如何进行分析,我目前还没有特别好的思路,希望有着方面经验的小伙伴可以一起交流。

出去就餐并且理解Express.js的基本知识

Going out to eat and understanding the basics of Express.js出去就餐并且理解Express.js的基本知识 原文:Going out to eat and understanding the basics of Express.js 译者:neal1991 welcome to star my articles-translator , providing you advanced articles translation. Any suggestion, please issue or contact me LICENSE: MIT 如果你曾经去过一个坐下来就餐的餐厅,那么你可以了解 Express 的基础知识。 但是,如果你刚刚开始构建你的第一个 Node.js 后端……你可能并不会很顺利。 是的 - 如果你曾经有过 JavaScript 经验,学习 Node 肯定更容易。 但是,在构建后端时面临的挑战与在前端使用JavaScript 时所面临的挑战完全不同。 当我学习Node时,我选择了困难的方式。 我一遍又一遍地学习电子书,写作教程和视频,直到我终于明白我为什么要做我正在做的事情。 有一个更简单的方法。 我打算用一个餐馆的比喻来解释你的第一个应用程序的四个关键部分。 Express.js 是一个组织你的代码的流行框架,我会为任何初学者推荐它。 稍后我会进一步解释。 下面是我们将会涉及到的四个关键部分: The require statements Middleware Routing App.

将Medium中的博客导出成markdown

Medium(需要翻墙访问)是国外非常知名的一个博客平台。上面经常有很多知名的技术大牛在上面发布博客,现在一般国内的搬运的技术文章大多数都是来自于这个平台。 Medium 文章格式显示地非常优雅,但是存在一个问题。众所周知,markdown已经是最受程序猿欢迎的文本编辑格式之一。但是Medium仅仅支持markdown格式导入,不支持markdown格式的导出。这也正是我当初开发这个插件export-medium的原因,现在这个项目是放在github上面的,欢迎大家多多star,或者pr。自己也花了5美金,注册了开发者账号,因为现在chrome对于不是商店的插件限制很严格,如果没上商店,一直有提醒,很麻烦。商店访问地址在这,需要翻墙访问。不过你可以手动安装: * 将 export-medium clone 或者下载到本地。 * 在 Chrome 浏览中打开chrome://extensions,加载已解压的拓展程序,选择项目文件夹 这两种方法都是可以支持安装的。目前这个插件的功能主要是把Medium上面的文章解析成 markdown 格式的文本,用了一个简单的库去渲染(事实上我觉得挺鸡肋的),然后你只要点击一个按钮就可以把文本复制到剪切板,就可以复制到编辑器了,是不是很方便。 目前可能很多页面做的不是特别好看,欢迎大家感兴趣的可以试用或者向我提建议。 仓库地址: https://github.com/neal1991/export-medium (喜欢的还请多多star!!!)

Mongoose中document和object的区别

这个问题其实是mongoose非常常见的问题,经常有很多以前没遇到这个问题的人都会被这个问题弄得怀疑人生。我们先介绍一些问题的背景。先看下面一段代码: router.get('/', function(req, res, next) { // res.render('index', { title: 'Express' }); const model = mongoose.model('realestate'); const queryCretia = {}; model.find(queryCretia, (err, docs) => { res.render('index', { title: 'express', docs: docs }) }) }); <!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> <!-- <%= docs %> --> <ul> <% docs.forEach(function(doc){ %> <li><%= doc.type %></li> <% }) %> </ul> </body> </html> 在第一段代码中,通过model.

JavaScript是如何工作的:引擎,运行时间以及调用栈的概述

JavaScript是如何工作的:引擎,运行时以及调用栈的概述 原文:How JavaScript works: an overview of the engine, the runtime, and the call stack 译者:neal1991 welcome to star my articles-translator , providing you advanced articles translation. Any suggestion, please issue or contact me LICENSE: MIT 随着JavaScript变得越来越流行,团队在多个层级都对它进行利用-前端,后端,混合应用,嵌入式设备以及更多。 正如GitHut stats所展示的那样,JavaScript是Github上面最活跃以及总Push次数最多的语言。在其它类别中也不会落后太多。 (获取最新的 GitHub language stats). 如果项目对于JavaScript越来越依赖,这意味着为了构建好的软件开发者必须利用这个JS提供的一切并且对于生态系统的内部有着更深的理解。 因此,尽管每天有很多开发者在使用JavaScript,但并不知道内部到底发生了什么。 概览 几乎每个人都已经听说过V8引擎的概念,并且很多知道JavaScript是单线程的或者它是使用一个回调队列的。 在这篇博文中,我们将会详细讲述所有概念并且解释JavaScript是如何真正运行的。在了解这些细节之后,你将能够写出能够适宜地利用提供的API的更好的,非阻塞的app。 如果对于JvaScript来说还不是很了解,这篇博文将会帮助你理解为什么JavaScript和别的语言相比如此“奇怪”。 如果你是一个有经验的JavaScript开发者,希望这篇文章能够让你对你每天使用的JavaScript Runtime是如何真正工作的。 JavaScript 引擎 最流行的JavaScript引擎的例子之一就是谷歌的V8引擎。比如Chrome以及Node.js内部就是使用V8引擎。下面是一个简单的视图示例: 引擎主要由两个部分组成: 内存堆——这是内存分配发生的地方 回调——这是你代码执行时的栈帧。 Runtime 有很多浏览器中的API几乎都被JavaScript开发者使用过(比如:’setTimeout’)。然而这些API并不是由引擎提供的。 那么,它们是从哪来的呢? 事实证明这有一点复杂。 因此,虽然我们有引擎但实际上是有更多。我们有那些由浏览器提供的Web API,像DOM, AJAX, setTimeout以及更多。