---
title: 自建 Anki 同步服务器遇到的坑
date: 2018-02-19T15:00:00+08:00
tags:
  - 技术

---

一直以来都想着拯救我的 broken English，好准备接下来的六级考试。前段时间在 V2EX 看到一位大神分享了一份实用的英语学习指导 [https://github.com/byoungd/English-level-up-tips-for-Chinese](https://github.com/byoungd/English-level-up-tips-for-Chinese)，遂被种草。同时我也认识到了自己单词量的匮乏，想通过背单词的方式把基础的词汇攒起来。恰好教程提供了一份「麦克米伦7000高频词」的 Anki 牌组，便打算从这里开始。

添加了一个 6000+ 卡牌的牌组的后果是，媒体文件同步AnkiWeb的时候巨慢无比，毕竟AnkiWeb的服务器远在德国，这也使我催生了搭建自己的 Anki 同步服务器的想法。

参考 [手把手教你搭建自己专属的Anki服务器 - 简书](https://www.jianshu.com/p/c50e3feec878) 这篇教程，我很快在VPS上把这玩意儿搭建好了，但同步的时候却莫名奇妙出现 500 错误的问题，查看日志，发现了如下的报错信息：

```
ERROR:root:CollectionThread[/home/anki/anki/collections/qing/collection.anki2]: Unable to uploadChanges(*[], **{}): 'latin-1' codec can't encode characters in position 51-52: ordinal not in range(256)
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/AnkiServer-2.0.6-py2.7.egg/AnkiServer/threading.py", line 99, in _run
    ret = self.wrapper.execute(func, args, kw, return_queue)
  File "/usr/local/lib/python2.7/dist-packages/AnkiServer-2.0.6-py2.7.egg/AnkiServer/collection.py", line 58, in execute
    ret = func(*args, **kw)
  File "/usr/local/lib/python2.7/dist-packages/AnkiServer-2.0.6-py2.7.egg/AnkiServer/apps/sync_app.py", line 651, in run_func
    res = handler_method(**keyword_args)
  File "/usr/local/lib/python2.7/dist-packages/AnkiServer-2.0.6-py2.7.egg/AnkiServer/apps/sync_app.py", line 113, in uploadChanges
    processed_count = self._adopt_media_changes_from_zip(data)
  File "/usr/local/lib/python2.7/dist-packages/AnkiServer-2.0.6-py2.7.egg/AnkiServer/apps/sync_app.py", line 179, in _adopt_media_changes_from_zip
    open(file_path, 'wb').write(file_data)
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 51-52: ordinal not in range(256)
```

经过测试发现，媒体文件名是全英文的时候不会存在这个问题，当路径中出现中文的时候就会报错。

计算媒体文件夹完整路径的字符串的长度，恰好是50，报错位置是 `51-52`，也印证了我之前的判断。

搜索各类 py2.7 关于编码问题的文章，研究了一晚上，百思不得其解。。

上午我在本地的 Ubuntu 也搭建了一个测试，竟一切正常。所以问题应该和服务器与本地环境的差异有关。

几经周折，终于发现了问题所在，是`Python2`在不同操作系统的文件系统编码的差异导致的问题。

本地的系统的文件系统编码是`UTF-8`

![271065569.png](https://cdn.zgq.me/assets/posts/2018/anki-server-problems/271065569.png)

而我的VPS上的系统的文件系统编码是`ANSI_X3.4-1968`

![1819120441.png](https://cdn.zgq.me/assets/posts/2018/anki-server-problems/1819120441.png)

所以最终我给文件路径加上了一个 `UTF-8` 编码的转换，解决了这个问题。

具体操作如下：

修改文件 `vim /usr/local/lib/python2.7/dist-packages/AnkiServer-2.0.6-py2.7.egg/AnkiServer/apps/sync_app.py` 的 176 行

```
把
file_path = os.path.join(self.col.media.dir(), filename)
改为
file_path = os.path.join(self.col.media.dir(), filename).encode('utf-8')
```

重新编译 pyc

```bash
python -m py_compile /usr/local/lib/python2.7/dist-packages/AnkiServer-2.0.6-py2.7.egg/AnkiServer/apps/sync_app.py
```

再重启一下服务

```
supervisorctl restart anki-server
```

同步恢复正常了。

这改法的缺点是，如果用 pip 升级原来的包的话，原本的记录就没了，之后再考虑重新部署一下吧╮(╯▽╰)╭

第一次近距离感受 py2 的坑，还是很酸爽的233333

搜索的时候发现有不少的哥们也遇到了这个问题

![3681576606.png](https://cdn.zgq.me/assets/posts/2018/anki-server-problems/3681576606.png)

-------------补充---------------

delete删除的部分也会遇到这个问题，重新修改一下即可

参考文章：[Python2编码之殇 | nMask's Blog](https://thief.one/2017/02/16/%E8%A7%A3%E5%86%B3Python2-x%E7%BC%96%E7%A0%81%E4%B9%8B%E6%AE%87/)
