背景

我在项目中使用阿里云OSS存储文件,导出时导出为csv格式的文件。然而打开时总是存在中文乱码。
通过网上的一番搜索,大抵是使用记事本先打开csv文件,然后再保存为ANSI格式,然后再用excel打开时就不存在乱码了。但这不是程序里的解决方式,我们不可能提供一个半成品的导出文件供用户使用。经过一番网上搜寻,发现问题的根源在于UTF8的BOM信息头。
本来UTF8是不需要BOM头的,这就不得不说到微软的可恶了。

BOM简介

BOM中文译作”字节顺序标记”,UTF8本不需要BOM来表明字节顺序,但WINDOWS用BOM来标记文件文件的编码方式。BOM的UTF8编码是”EF BB BF”,所以如果接收者收到以”EF BB BF”开头的字节流,就知道这是UTF8编码了。WINDOWS系软件保存的UTF8编码的文件需要文件的开头保有这个BOM字符。

问题解决

项目中使用OSS存储文件,存储成功后返回一个签名过的文件url地址,前端再根据这个url去请求获取文件。由于前端是直接使用”window.open(url)”的方式,直接新开窗口访问链接,下载下来的文件默认是UTF8编码的,因此使用excel打开时需要BOM字符来表明是UTF8编码,否则其中的中文则会产生乱码。

因此,在OSS存储时,我们就需要额外添加BOM头一并存储。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
OSSClient client = new OSSClient(ossHelper.getEndPoint(), ossHelper.getAccessKeyId(),
ossHelper.getAccessKeySecret());
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentType("application/vnd.ms-excel");
objectMetadata.setContentEncoding("UTF-8");
objectMetadata.setContentDisposition("attachment; filename=" + fileName + ".csv");
// 增加BOM头信息
String bom = new String(new byte[] { (byte) 0xEF, (byte) 0xBB,(byte) 0xBF });
try {
// 以同样的编码获取字节流
client.putObject(ossHelper.getBucketName(), fileName,
new ByteArrayInputStream((bom + exportFileString).getBytes("UTF-8")), objectMetadata);
}catch (UnsupportedEncodingException e){
e.printStackTrace();
}

最近在看《Java编程思想》,记录一些觉得有必要整理的内容。本篇就简要分析一下Java并发编程中遇到的volatile关键字。

并发编程中,我们通常会遇到以下三个概念:原子性可视性以及有序性
volatile关键词正式对应可视性这个概念。

可视性是指,当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改的值。即便使用了本地缓存,volatile修饰的域也会立即被写入到主存中,而读取操作就是发生在主存中。
而相应地,非volatile域上的原子操作不必刷新到主存中去,因此其他读取该域的任务也不必看到这个新值。

然而,要区分原子性和可视性是不同的概念。volatile关键字能保证可视性但并不能保证操作的原子性。如果一个域的值依赖于它之前的值,例如递增一个计数器,或者某个域的值收到其他域的值的限制,例如Range类的lower和upper边界就必须遵循lower<=upper的限制,那么只用volatie修饰是不起作用的。保持原子性或说同步,第一选择应该是使用synchronized关键字,同步也会导致向主存中刷新,因此synchronized防护的方法或语句块内的域就不必使用volatile修饰了。

阅读全文 »

背景

本篇博文分享下使用树莓派结合百度语音合成API进行当前时间和天气自动播报功能的实现。转载请注明出处。

阅读本文需要你有一定的Linux编码水平(主要是Bash)及一些python基础。
这个项目的想法初衷是希望树莓派能够定时地将一些信息通过蓝牙播放设备自动播放出来,初期先播放时间和天气,能够代替一个闹钟了呢,哈哈~~

方案设计

方案很简单,设置定时任务,将运行时时间获取到,并同时获取当前天气状况,然后使用百度语音合成API将获取到的信息转换成语音通过蓝牙连接的播放设备播放出来。

获取天气也使用了百度开放平台提供的接口,因此一共需要有百度开放平台和语音合成API两个接口的调用权限。

开放平台注册
语音合成API注册

获取天气

我们先来获取天气信息,API的返回结果结构如下:

我舍弃了穿衣指数什么的,只获取天气数据。

阅读全文 »

背景

阅读本文需要你有一定的Linux编码水平,至少能够读懂Bash,对于一个树莓派玩家来说这都不是事儿了吧。

树莓派使用Micro SD卡来作为系统存储,而SD卡又很容易坏,当我们辛苦配置好的树莓派工作环境因为SD卡挂掉,重装时各种配置又要来一趟真的很奔溃,关键还不记得当初一个个都配置了啥了。。

我最近在配置树莓派时就遇到了类似的问题,因为挂载移动硬盘出了点差错(下文会说),导致系统启动失败,以为是系统被我折腾坏了,格了好几遍SD卡重装,每次都要把软件、配置都重来一遍,于是索性将第一次开机后必做的几件事写成了初始化脚本传到github了,重装后直接下下来执行下就解决了。方便多了,再也不怕重装系统。

顺便记录下导致我写这个自动化初始化树莓派脚本的最初问题。

问题

一开始我的树莓派是没有显示器的,都是使用VNC远程连接。最近在配置了不知道哪个东西后,树莓派就连不上了,反复重装过几次,经过一番配置之后重启后必然又连不上。后来搞了跟显示器的连接线,启动后发现树莓派进入了”emergency mode“,网上查了下,解决方案对我都不适用,最后我才发现,原来是我在设置自动挂载移动硬盘的时候出错,导致系统启动失败。我的硬盘是NTFS格式的,而树莓派不能直接挂载ntfs格式,需要先安装一个ntfs-3g工具。由于重装过系统,忘记了安装这个,直接将挂载命令写入了fstab文件而又没有验证是否能够挂载成功(毕竟挂载过太多次了,想当然以为没问题了)。

阅读全文 »

alert(28443422041709109)会输出什么?

背景

上一篇博文里我记录了一个诡异的前后端数据不一致的问题,最终定位为前端js精度丢失。但只说了原因及结论并没有深入研究这个问题。
这一篇博文准备在此基础上,深入探寻一番,彻底弄清楚这个问题发生的本质。

引子

让我们先来看几个小问题热热身:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
## js环境下运行,输出结果是什么?
1. console.log(1 === 1.0);
2. console.log(0.1 + 0.2 === 0.3);
3. console.log(9007199254740992);
4. console.log(9007199254740992 + 1);
5. console.log(9007199254740992 + 2);
6. console.log(9007199254740992 + 3);
7. console.log(9007199254740992 + 4);
... ...

浏览器环境下运行下看看是不是有点颠覆了三观?

阅读全文 »

这是我转战JAVA以来的第一篇博文,纪念一下。

背景

组织变动,转战JAVA差不多有一个月了,做了一个小需求,遇到一个诡异的问题。本文记录一下排查经过,顺便提醒自己后续开发中避免这样的坑。

问题描述

JAVA后端需要给前端返回一串数据,其中有个交易订单号,该交易订单在返回给前端后跟后端吐出的值不一致。直观地来看,最后的几个数字被0取代了。
一开始以为取错了交易订单号,然而从DEBUG的过程来看,后端的处理结果没有问题,返回了正确的结果给前端。
如下图所示:

当时也并没有纠结问题的根本原因,下意识地将交易订单号类型从List<Long>换成了List<String>返回就正常了。

发布以后觉得这里可能是个坑,于是研究了一下果然是有缘由的。

阅读全文 »

背景

我们经常需要批量地执行很多mysql命令,这时候如果直接在mysql终端下执行,效率很低。如果借助Shell来进行自动化地操作,则会大大方便我们操作。这里就记录一下我用来进行数据库表重建的方法。

准备mysql脚本

首先我们要准备相应的mysql命令脚本,以便shell调用。
如:

1
2
3
4
5
6
7
8
9
10
11
12
DROP TABLE IF EXISTS `attachment`;
CREATE TABLE `attachment` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`init_name` varchar(100) NOT NULL,
`file_path` varchar(100) NOT NULL,
`gmt_create` datetime NOT NULL DEFAULT now(),
`gmt_modified` timestamp NOT NULL DEFAULT now(),
`creator` varchar(100) NOT NULL DEFAULT 'system',
`modifier` varchar(100) NOT NULL DEFAULT 'system',
`is_deleted` char(1) NOT NULL DEFAULT 'N',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=460 DEFAULT CHARSET=utf8;

正常写mysql命令即可。

阅读全文 »

背景

安装Mysql不知道安装了多少次了,但是每次装的时候都要重新查看文档,每次又会遇到不一样的问题。还是记录一下安装步骤吧,省的每次都要重新翻文档了。

安装步骤

以安装mysql5.6版本为例,其他版本可以下载官网对应版本,然后查看附带的INSTALL文档。

  1. 下载
    wget http://cdn.mysql.com/archives/mysql-5.6/mysql-5.6.26-linux-glibc2.5-x86_64.tar.gz

  2. 解压并改名
    tar -xvf mysql-5.6.26-linux-glibc2.5-x86_64.tar.gz && mv mysql-5.6.26-linux-glibc2.5-x86_64 mysql

  3. 创建用户组

    1
    2
    3
    4
    5
    #建议将mysql安装在/usr/local目录
    mv mysql /usr/local
    cd mysql
    sudo groupadd mysql
    sudo useradd -r -g mysql mysql
  4. 修改权限

    1
    sudo chown -R mysql:mysql *
    阅读全文 »

已读



胡适先生这本著作更多的在于考据,我等凡人看的一头雾水。。很多上古文字直接贴一段了事了,根本不鸟你看懂不懂,毕竟北大教科书+_+



不同于王小波的小说,王小波的这本杂文集,没有小黄文,没有太多不正经,没有太多晦涩难懂的东西,但一如既往的黑色幽默。如果说小说是虚构一个故事,将想说的话和道理掩盖在故事里让你去一层层揭示,那么杂文就是明白无误的针对某个事或观点发表自己的意见,将想说的话和道理平铺直叙的表露给读者。看这本书就像人说的,“读一本好书就像跟一个要好的朋友聊天”,小波先生在这本文集里将他的人生观价值观向读者一一道来。应该会看第二遍。我读王小波比较迟,也读的比较少,但算是一见如故的一个作家。第一本大概是《未来世界》,那时候的书评已经找不到了。初看一个陌生的写作者就跟与人交往一样,会有磨合期,你不了解他的生平、观点以及文风,就很难理解他文字的意图,就像王小波的黑色幽默,乍一看时你闹不懂他是正经还是不正经。王小波97年去世享年45岁,在这本文集里,谈论生活态度时,他说“我刚刚过44岁生日,在这个年龄上给自己做结论似乎还为时过早。但我总觉得,我这一生绝不会向虚无投降。我会一直战斗到死”,读来不免扎心不已。



这是一本很有趣的科普书,以趣味的口吻讲述了进化论对人体如今这个样子是怎样塑造的。对进化论我一直很有兴趣,记得高中时的三大愿望是去研究微观、宇观或者生命起源。迫于现实的压力最终阴差阳错的走上了程序员的道路。其实从某方面来说计算机和人体很相似,计算机是0,1的二进制,人体其实也是一台高精密的仪器,是RNA的四进制’机器’。然而,某种程度上我对进化论还是持有怀疑的。在看过很多大自然的奇迹,诸如人体、诸如蝙蝠的声呐回声定位、蜜蜂的蜂巢、向日葵的种子呈对数螺线分布(星系、台风、花朵、海螺都是这样)…等等一系列有如神迹一般的现象,更不用说人体构成的复杂与精密。让人不生出冥冥之中有神创万物之感真的很难。进化论说,一些微小的变异加上漫长的时间演化,就可以造成这些神迹。果真是印证了那一句话,”时间是解决一切问题的所在”。另一方面,生物的活动,冥冥中都是基因在起作用,我们都是基因的奴隶,我们的生生不息的繁殖都是为了基因的延续,然而基因的永续延续又是为了什么呢?



“人生在世最大的两个危险,一个是过早死去,另一个是活得太久”。看到这句真的是”振聋发聩”,有多少人期盼长寿,又有多少人会意识到过于长寿可能带来的穷困潦倒的老年生活危机?”理财要趁早,养老计划要趁早”是我读该书获得的收获,至于怎样理财投资,路漫漫其修远。


时生

一如既往的温情,让坐在办公桌前的我都眼泛热泪。”能来到这个世上就已经很美好”,能跟自己的儿子一起胡闹,成长,体验应该不错。也许因为有了孩子之后感触更深,这本书带给我的感动比《解忧杂货铺》更强烈。


无声告白

“我们终此一生,就是要摆脱他人的期待,找到真正的自己”,曾经我也是如同莉迪亚一样在殷切的期望下成长,好在万金油的我能够承受。有时候家庭就是一个人的宿命,我们要做一个好儿子、好丈夫、好父亲,s哪有多少机会去做真正的自己。虽然我现在依然摆脱不了他人的期待,但我希望我以后对下一代的教育不要再像我们的父辈一样,充分引导让孩子选择如何正确地做他自己即可。

阅读全文 »