archive-us.com » US » B » BITFOC.US

Total: 1

Choose link from "Titles, links and description words view":

Or switch to "Titles and links view".
  • Bit Focus
    tiles suit tiles copy tiles i 2 groups completed numeric groups suit copy tiles if groups value is not None result append i groups return result def numeric groups with side waits suit suit tiles result for i in xrange 8 找出一个搭子 去掉之后试试剩下的能不能全凑成面子 if suit tiles i and suit tiles i 1 copy tiles suit tiles copy tiles i 1 copy tiles i 1 1 groups completed numeric groups suit copy tiles if groups value is not None result append i groups return result def numeric groups with middle waits suit suit tiles result for i in xrange 7 找出一个两坎 去掉之后试试剩下的能不能全凑成面子 if suit tiles i and suit tiles i 2 copy tiles suit tiles copy tiles i 1 copy tiles i 2 1 groups completed numeric groups suit copy tiles if groups value is not None result append i groups return result 最后的各个函数返回值类型均为元素结构为 对子或搭子首张序数 面子分组 的列表 这样所有听牌与字牌可能相关的情况讨论完毕 而无字牌或字牌全为刻子的情况则会在 下篇 中讨论 数学证明的边界扩张 http blog bitfoc us p 520 http blog bitfoc us p 520 May 16 2014 01 42 10 0000 证明与反驳 这本书 虽然不是什么数学著作 讨论的也不是改变世界的重要定理 不过围绕着 多面体面棱角个数关系的那个欧拉定理 讲得风生水起 还是挺有意思的 书里提到数学研究的一个策略 对于一个已经证明的定理 通过想方设法扩张其证明步骤中的条件 边界 也许可以把定理从一个特殊形式扩展到普遍形式 就像写程序库一样 总希望写出来的东西能尽可能满足各种不同参数下的需求 虽然在软件行业这么干往往会把自己玩死 比如看看 费马平方和定理 第四步的结论 请一定看完前四步证明 后文的内容均是修改此证明 对于互素的任意整数 a 和 b a 2 b 2 的每一个因子也都能表示为两个整数的平方和的形式 这一步虽然内容上有不少平方和 不过还不是平方和定理实际内容 只是一步中间引理 然而即使如此证明看起来也挺费神的 要扩展这个定理的话 可以从多个不同角度出发 对于两个整数平方和成立 对于三个整数平方和是否成立 或者 本文将采取的方式如下 给定正整数 v u 对于互素的任意整数 a 和 b v a 2 u b 2 的每一个因子也都能表示为 v p 2 u q 2 的形式 然后试着往原来的证明过程中套 看看会发生什么 第一步用到了 名字巨长的恒等式 这个等式说两个平方和的乘积还是一个平方和 在代数学中有个术语叫封闭性 比如整数集里面任取两个元素出来进行加减乘运算 结果还是整数 除就不一定了 那么加减乘这三种运算 三个二元函数 对于整数集就是封闭的 类似的 上述恒等式说明在所有能表示成两个整数平方和的数的集合内 乘法是封闭的 那么这个结论能否运用到形如 v a 2 u b 2 的整数呢 很快就能验证 v a 2 u b 2 v c 2 u d 2 v 2 ac 2 vu ad 2 bc 2 u 2 bd 2 v 2 ac 2 u 2 bd 2 2 uv abcd vu ad 2 bc 2 2 uv abcd vac ubd 2 vu ad bc 2 并不是 v p 2 u q 2 而是 p 2 vu q 2 的形式 真糟糕 第一步封闭性就阵亡了 那么今天的内容就到这里 谢谢大家观看 我们下期节目再见 噢等等 我觉得这个扩张还可以抢救一下 只要作出一点小牺牲 把内容收缩一点就好 令 v u 之一等于 1 也就是说变成比如 给定正整数 u 对于互素的任意整数 a 和 b a 2 u b 2 的每一个因子也都能表示为 p 2 u q 2 的形式 还是可以继续下去的 至少 a 2 u b 2 c 2 u d 2 ac ubd 2 u ad bc 2 ac ubd 2 u ad bc 2 确实是两种 p 2 u q 2 的形式 虽然 p 2 u q 2 这么个不对称的形式肯定会让强迫症加数学美学狂热者想要砸显示器 不过也没办法 将就着到第二步 给定整数 u 对于任意整数 a b 不要求互素 如果形如 a 2 u b 2 的整数能被可表示为 p 2 u q 2 的素数整除 则其商也能表示为以上形式 如果想挑战一下 别继续滚页面 自己推演一下 答案将在三行之后开始揭晓 第一行 回头再看一眼命题第一句 是 给定任意 正 整数 u 这个正是很重要的 虽然第一步恒等式对于任意整数 u 都能成立 但若 u 不是正数 这一步证明会有一个不严格的地方 而第四步中若 u 不是一个正数则完全是硬伤 第二行 来个简单有效的反例 在 u 1 时 随便令 a 和 b 为两个不同的奇素数 显然 a 2 b 2 会是个偶数 能被 2 整除 但 2 没法写成平方差的形式 第三行 具体为什么 到最后一步再说吧 接下来继续证明 因为 p 2 u q 2 能整除 a 2 u b 2 那么它肯定能整除 u q 2 a 2 u b 2 从而也就能整除 u q 2 a 2 u b 2 a 2 p 2 u q 2 ubq 2 ap 2 ubq ap ubq ap 根据 算术基本定理 惟一性证明的引理 作为素数的 p 2 u q 2 能整除 ubq ap 与 ubq ap 两个数之积 那么必须能至少整除其中某一个 假设整除的是 ubq ap 那么 a 2 u b 2 p 2 u q 2 ap ubq 2 u aq bp 2 两边同除以 p 2 u q 2 2 得 a 2 u b 2 p 2 u q 2 ap ubq p 2 u q 2 2 u aq bp p 2 u q 2 2 显然等式左边是一个整数 右边有两项 其中第一项根据假设是一个整数 那么第二项也得是个整数 因此 第二项似乎正好是 ux 2 的形式 证明搞定 似乎又糟糕了 u aq bp p 2 u q 2 2 是一个整数并不能说明 aq bp p 2 u q 2 是一个整数 有一种可能是 p 2 u q 2 恰能整除 u 而不是能整除 aq bp 使得这一项最终为整数 如果是这样 第二项的 u 这个因子就会被削减得小一点 因而形式并不成立 万幸地是 很显然 u 怎么可能被比 u 自己还大的素数 p 2 u q 2 整除 p q 必须全不为 0 否则此数不是一个素数 因此它肯定比 u 大 但是刚才说了 在 u 为负数的情况下则不然 一个例子是 当 u 2 时 p q 分别取 p 2 q 1 代入得 2 2 2 1 1 2 可以整除 u 所以右边的第二项确实是满足要求的 刚才说到 p 2 u q 2 至少能整除 ubq ap 与 ubq ap 两者之一 并在假设能整除前者时命题成立 现在还得证明能整除后者的情况 若这种情况发生 则采用另一种表示方式 a 2 u b 2 p 2 u q 2 ap ubq 2 u aq bp 2 故伎重演 同除以 p 2 u q 2 2 结果 a 2 u b 2 p 2 u q 2 ap ubq p 2 u q 2 2 u aq bp p 2 u q 2 2 这样就安全通过第二步了 第三步 如果一个能表示为 a 2 u b 2 的整数被另一个不能表示为 a 2 u b 2 的整数整除 则它们的商也必有一个不能表示为 a 2 u b 2 的因子 这一步是反证法 没什么好说的 跟 Wiki 上证明的说辞类似 激动人心的最后一步终于来了 先剧透一下 其实 u 除了 1 之外 只有 2 能过这一步 也就是说到头来只多证明了所有形如 a 2 2 b 2 的整数的所有因子有同样形式 如果各位看官直觉敏锐 早先也许就能想到了 类似 u 1 的情况 在 u 2 时 如果 a 2 u b 2 是个偶数 因子 2 是不能写成 a 2 u b 2 的形式的 orz 不过 u 3 的时候能证明一个特性命题 虽然不那么完美 后述 最后一步其实也没什么花样 跟 Wiki 证明一样 设因子为 x 且 a mx c b nx d 则有 zx a 2 u b 2 Ax c 2 u d 2 中略 x 2 2 u x 2 2 x 2 1 u 4 z x 1 u 4 就是这个不等式右边坏事了 不等式右边必须是一个严格小于 x 的数 这样才使得 z x 必然成立 后面的证明用到的无穷下降法才能进行下去 如此以来只有 u 3 时才行 但是 u 又不能是负数 特别负得太厉害的时候 会导致右边是个负数 这样与 x 乘在一起的另外的因子也必须是负数 当把这个校正成正数的时候 硬伤来了 不等式的不等号由 变成了 这还怎么下降怎么收场 最后说一下 u 3 时的特别情况 还是回到刚才的不等式的右边 如果 x 是奇数的话 c 和 d 都不可能正好等于分数 x 2 因此不等式的符号就可以由小于等于换成严格小于 此时无穷下降法成立 也就是说 对于互素整数 a 和 b 所有形如 a 2 3 b 2 的奇数因子都能写成同样形式 如果 a b 是一奇一偶 a 2 3 b 2 是一个奇数 这样其所有因子都是奇数 如果 a b 是两个奇数 容易证明 a 2 3 b 2 能被 4 整除 但不可能被 8 整除 这造成了一个有趣的结论 该数的所有奇数因子 以及能被 4 整除的因子都能写成此形式 因为虽然 2 不能表示成 p 2 3 q 2 但是代入 p q 1 却正好等于 4 VerbalExpressions 与状态机词法分析器 http blog bitfoc us p 519 http blog bitfoc us p 519 May 06 2014 08 40 39 0000 VerbalExpressions 说到字符串检索分析替换修改自然会想到 正则表达式 不过这东西实在是一个只写语言 更改系统中一个一般复杂的正则表达式 传统的读懂代码然后替换一条语句或者加上一个分支或者参数的模式不管用 而是直接重写 就像清理一个塞满的垃圾桶 方法不是把垃圾一点点挖出来 而是整个倒掉再铺上新的垃圾袋 正则表达式有时太复杂了 一条语句一个调用就顶过一打的循环和分支 人们总会想到一些更节省脑细胞的方式来对付字符串 让机器理解人类的咒语 于是发明了 VerbalExpressions 下面是一个 JS 的例子 var tester VerEx startOfLine then http maybe s then maybe www anythingBut endOfLine 上面这一串等价于 http s www 这么个正则表达式 不过书写起来显得科学多了 如果需要更改逻辑 也很容易下手到底是什么地方需要增加或者减少一点什么 基于自动机的词法分析器 这个轮子很有启发性 于是乎想到以类似的方式构造个词法解析器 接口上的愿景是类似 var t Tokenizer t simpleSymbols operator ignore ignore t ignore r 上面都是单独的一个字符 接下来是循环的模式 loop DIGITS accept integer 以 0 9 循环的模式 接受为整数类型 startWith LETTERS loop LETTERS DIGITS accept identifier 以字母开头 数字和字母循环的模式 接受为标识符 接下来是保留字 fixed if fixed for 以及一些超过 1 字符的操作符 fixed operator fixed operator var inputString for i 0 i 10 i i 1 if i 3 0 print i var tokenArray t tokenize inputString console log tokenArray 看起来应该是这么回事 以这种方式构造出来的东西应该是一个状态机而不是一大波正则表达式形成的集群 因此首先得构造一个状态数据结构 作为一个演示就不弄太复杂了 它看起来类似 var State 0 nextState0 1 nextState1 a nextStateA 以上是一个超大状态映射表 将每个可能遇到的字符映射到下一个状态 这是最纯天然最暴力的状态机实现方法 但原则上应该将这些东西弄成一个函数 因为映射全部的字符 超过 2 16 个 Unicode 字符 是不现实的 所以先假定 测试数据只含有 ASCII 字符 以下是每个状态本身的性质 ignore true 这个状态被忽略 不引起错误 也不产生结果 如空格 type integer 接受的类型 如 integer identifier 接下来就要提供构造状态跳转网络的接口 即前面愿景中的 simpleSymbols ignore loop startWith fixed 等等 在编译原理中任何一个自动机都要有初始状态 在下面的代码中将命名其为 entryState 现实中还需要一个忽略状态 它会被称为 ignoreState 以下是 simpleSymbols 的实现 var entryState var ignoreState ignore true function simpleSymbols symbols name var symbolState type name for var i 0 i symbols length i var ch symbols i if entryState ch throw duplicate entry ch entryState ch symbolState 之前写的是链式操作 那就得返回 this 虽然目前这个函数并没有处于任何合适的对象上下文中 return this 如此以来 当调用 simpleSymbols operator 之后 entryState 的 这些字符各自对应的状态映射被指向相同的一个 symbolState 该状态的类型是 operator ignore 函数的实现如下 将 entryState 与 ignoreState 连接起来 function ignore symbols for var i 0 i symbols length i var ch symbols i entryState ch ignoreState ignoreState ch ignoreState return this 比较麻烦的是 startWith loop accept 这一组 可能还有其它的函数 accept 是最后收尾的 而在之前可能多次以不同的方式调用 startWith loop 并且 startWith 还能省略 这样产生了一些单个函数无法控制的状态转换 必须新弄一个什么来记录 var stateTrace function startWith symbols if stateTrace length throw already started var startState for var i 0 i symbols length i var ch symbols i if entryState ch throw duplicate entry ch entryState ch startState stateTrace push startState return this function loop symbols if stateTrace length 0 startWith symbols var currentState stateTrace stateTrace length 1 for var i 0 i symbols length i var ch symbols i currentState ch currentState return this function accept type if stateTrace length 0 throw pattern not started var currentState stateTrace stateTrace length 1 currentState type type stateTrace length 0 清空状态栈 return this 要点就是 stateTrace 这个状态栈保存当前进行到何处这一信息 loop 的实现有瑕疵 因为重复调用 loop 竟不会产生新的状态 这显然与直觉不一致 有兴趣的话可以尝试修复这个 bug 其它类似的函数就不赘述了 最后说说这个状态机的状态跳转都构造完成后 如何使用它来切割字符串 function tokenize input var state entryState var token 当前词元包含的字符 var result 处理下一个字符 function nextChar ch 如果下一个字符可以直接跳转状态 if state ch 那就跳转状态呗 state state ch if state ignore token push ch return 下一个字符跳不动了 看看这个状态是否有类型 即被接受 if state type 把之前临时存储的字符打包在一起 作为一个被接受的词元 result push token token join type state type 当前字符本身还需要被处理 return resetConsume ch 好吧 字符跳不动了 而当前停留的状态也不是一个接受状态 if state ignore 不过幸好这是需要忽略的 return resetConsume ch 万般无奈只能报错了 throw unexpected character 重置状态并处理接下来的第一个字符 function resetConsume ch token length 0 state entryState nextChar ch for var i 0 i input length i nextChar input i 当遍历所有字符后 可能停留在一个可接受的状态上 if token length 0 state result push token token join type state type return result 零碎的代码逻辑大概就是这样了 完整的例子请看 这里 就算是 Linux 命令行只要有爱就能剪辑 MAD 了吧 http blog bitfoc us p 518 http blog bitfoc us p 518 Mar 22 2014 13 03 30 0000 http zlo gs p neuront linux dakedo ai sae areba mad dekiru yone 索引统计与 Python 字典 http blog bitfoc us p 517 http blog bitfoc us p 517 Jan 01 2014 05 16 40 0000 索引引擎的基本工作原理便是倒排索引 即将一个文档所包含的文字反过来映射至文档 这方面算法并没有太多花样可言 为了增加效率 索引数据尽可往内存里面搬 此法可效王献之习书法之势 只要把十八台机器内存全部塞满 那么基本也就功成名就了 而基本思路举个简单例子 现在有以下文档 分词已经完成 以及其包含的关键词 doc a word w word x word y doc b word x word z doc c word y 将其变换为 word w doc a word x doc a doc b word y doc a doc c word z doc b 写成 Python 代码 便是 doc a id a words word w word x word y doc b id b words word x word z doc c id c words word y docs doc a doc b doc c indices dict for doc in docs for word in doc words if word not in indices indices word indices word append doc id print indices 不过这里有个小技巧 就是对于判断当前词是否已经在索引字典里的分支 if word not in indices indices word 可以被 dict 的 setdefault key default None 接口替换 此接口的作用是 如果 key 在字典里 那么好说 拿出对应的值来 否则 新建此 key 且设置默认对应值为 default 但从设计上来说 我不明白为何 default 有个默认值 None 看起来并无多大意义 如果确要使用此接口 大体都会自带默认值吧 如下 for doc in docs for word in doc words indices setdefault word append doc id 这样就省掉分支了 代码看起来少很多 不过在某些情况下 setdefault 用起来并不顺手 当 default 值构造很复杂时 或产生 default 值有副作用时 以及一个之后会说到的情况 前两种情况一言以蔽之 就是 setdefault 不适用于 default 需要惰性求值的场景 换言之 为了兼顾这种需求 setdefault 可能会设计成 def setdefault self key default factory if key not in self self key default factory return self key 倘若真如此 那么上面的代码应改成 for doc in docs for word in doc words indices setdefault word list append doc id 不过实际上有其它替代方案 这个最后会提到 如果说上面只是一个能预见但实际上可能根本不会遇到的 API 缺陷 那么下面这个就略打脸了 考虑现在要进行词频统计 即一个词在文章中出现了多少次 如果直接拿 dict 来写 大致是 def word count words count dict for word in words count setdefault word 0 1 return count print word count hiiragi kagami hiiragi tukasa yosimizu kagami 当你兴致勃勃地跑起上面代码时 代码会以迅雷不及掩脸之势把异常甩到你鼻尖上 因为出现在 操作符左边的 count setdefault word 0 在 Python 中不是一个左值 怎样 现在开始念叨 C艹 类型体系的好了吧 因为 Python 把默认的字面常量 等价于 dict 就认为 dict 是银弹的思想是要不得的 Python 里面各种数据结构不少 解决统计问题 理想的方案是 collections defaultdict 这个类 下面的代码想必看一眼就明白 from collections import defaultdict doc a id a words word w word x word y doc b id b words word x word z doc c id c words word y docs doc a doc b doc c indices defaultdict list for doc in docs for word in doc words indices word append doc id print indices def word count words count defaultdict int for word in words count word 1 return count print word count hiiragi kagami hiiragi tukasa yosimizu kagami 完满解决了之前遇到的那些破事 此外 collections 里还有个 Counter 可以粗略认为它是 defaultdict int 的扩展 简易配置 gunicorn http blog bitfoc us p 516 http blog bitfoc us p 516 Oct 30 2013 07 29 13 0000 引子 单纯 gevent 跟 nodejs 一样有个问题是如果服务器有大的同步计算 比如压缩一张图片什么的 需求时 服务器会很卡 这也不能怪它们 因为本来它们的长处是 IO 异步化 同步计算卡住是 缺陷 特性之一 然 或荐 基独搅受 gunicorn 以解此困 只是其首页上例子意味不明 各种文档文章都说要编写一些离奇复杂的配置文件 然后跑个语焉不详的 hello world 并没能明示重点问题 正文 嘛 一番探索之后配了下面一个用例 Flask import time import flask app flask Flask name app route int n def root n time sleep 2 i n 2 while 1 i if n i 0 return not prime i 1 return prime if name main app run port 8000 这个例子里面兼顾了长 IO 用睡眠去模拟 跟大计算 算请求的数是不是个素数 把这货在控制台裸着启动起来 然后用 apache benchmark 来一发 如果觉得后面请求参数里那个素数不够大 可以自行算一个大的替换 ab n 500 c 50 localhost 8000 16785407 当然了 c 50 这个参数纯是卖萌的 因为上面这代码自身根本异步不起来 结果自然是惨不忍睹 重点两行在测试机上表现如下 Time per request 131417 472 ms mean Time per request 2628 349 ms mean across all concurrent requests 平均单个请求耗时 2 6 秒以上 其中 2 秒是睡过去的 剩下 0 6 秒是计算 也就是说 IO 时间与计算时间大概的比例是 3 1 安装 gunicorn 可以直接通过 pip 安装 简单容易 就不废话了 下面上 gunicorn 平装版 把上面的文件保存为 test py 在控制台中执行 gunicorn w 4 test app 这个是说 开 4 个进程跑 test 模块下的 app 就是文件里全局定义的 app 变量啦 现在再开 ab 来一炮 参数完全相同 结果是 Time per request 33150 026 ms mean Time per request 663 001 ms mean across all concurrent requests 从结果上来看差不多就是裸跑的 1 4 了 因为开了 4 个进程一起搅嘛 虽然有 4 个进程睡睡醒醒轮番搞 但没有异步 IO 的支持 进程睡着就不干事了 作为要榨干 worker 进程以及 CPU 使用率的系统管理员来说这可不能忍 于是继续折腾个 gevent 进去好了 两者互补 相得益彰 不过用 gunicorn 就不需要在文件最开始打猴子补丁了 gunicorn 有个参数直接让 gevent 嵌入进程 gunicorn w 4 k gevent test app 再来一发 ab 结果是 Time per request 9724 214 ms mean Time per request 194 484 ms mean across all concurrent requests 嘛 算是还看得过去的数据了 补充说明 绑定其它端口 gunicorn b 0 0 0 0 8000 w 4 k gevent test app 我没有定义在全局的 app 我的 app 需要特殊的初始化方式 假如在文件里以一个特别的函数初始化 app 比如 def init app arg0 arg1 return app 可以如此法启动 gunicorn b 0 0 0 0 8000 w 4 k gevent test init app value0 value1 看起来有点类似与传命令行参数 感谢 双木成林 对这篇文章的指导与建议 记一些 没 有意义的 reduce 用法 http blog bitfoc us p 515 http blog bitfoc us p 515 Sep 30 2013 03 26 41 0000 reduce 函数 其中 Python 中 reduce 作为全局函数出现 而 Javascript 中则是 Array 的成员函数 大量的用 reduce 来做累加累乘之类的例子就不说了 这里探讨一个特殊的用例 前端经常会需要将页面中用户填写的一些内容打包成 JSON 字典 比如一个注册页面片段 div input id email placeholder Email input id password placeholder Password input id conform password placeholder Confirm Password input id address placeholder Address input id phonenum placeholder Phone Number button id subm Submit button div script document getElementById subm onclick function var inputValues email document getElementById email value password document getElementById password value address document getElementById address value phonenum document getElementById phonenum value process inputValues script 以后每次这个表单多一项时 构造 inputValues 时就会多一项 代码维护会很烦 如果能这样写的话可能会好一些 var inputValues k document getElementById k value for k in email password address phonenum 可惜 Javascript 里面没有温暖人心的 dict comprehension 于是 就有了下面这种 reduce 替代品 终于正题了 var inputValues email password address phonenum reduce function obj item obj item document getElementById item value return obj 看来 Python 在语法灵活度上的优势已经虐掉 filter map 函数两条街 而且 dict comprehension 还能顺带打压一下 reduce 也难怪 Guido 会挑明了说 他不喜欢这些函数 不过他不喜欢 reduce 主要是这东西太灵活 一眼看不明白 能一眼看明白的都直接用 sum 去了 比如上面这个用例如果没有铺垫 要理解还得废点草稿纸 用来提升 bignity 倒是不错 而下面这个例子也是 Python some dict x y a 10 z b 20 reduce lambda obj key obj get key dict x y a some dict 嘛 是否允许在项目里面使用类似的机巧代码 也许是比 哪个语言好 更值得每月探 zheng 讨 chao 的问题 或是指定一些固定用法作为范式 像背乘法口诀表一样背下来也许也不错的说 Flask MongoDB 搭建简易图片服务器 http blog bitfoc us p 514 http blog bitfoc us p 514 Sep 07 2013 04 10 57 0000 前期准备 通过 pip 或 easy install 安装了 pymongo 之后 就能通过 Python 调教 mongodb 了 接着安装个 flask 用来当 web 服务器 当然 mongo 也是得安装的 对于 Ubuntu 用户 特别是使用 Server 12 04 的同学 安装最新版要略费些周折 具体说是 sudo apt key adv keyserver hkp keyserver ubuntu com 80 recv 7F0CEB10 echo deb http downloads distro mongodb org repo ubuntu upstart dist 10gen sudo tee etc apt sources list d mongodb list sudo apt get update sudo apt get install mongodb 10gen 如果你跟我一样觉得让通过上传文件名的后缀判别用户上传的什么文件完全是捏着山药当小黄瓜一样欺骗自己 那么最好还准备个 Pillow 库 pip install Pillow 或 更适合 Windows 用户 easy install Pillow 正片 Flask 文件上传 Flask 官网上那个例子居然分了两截让人无从吐槽 这里先弄个最简单的 无论什么文件都先弄上来 import flask app flask Flask name app debug True app route upload methods POST def upload f flask request files uploaded file print f read return flask redirect app route def index return doctype html html body form action upload method post enctype multipart form data input type file name uploaded file input type submit value Upload form if name main app run port 7777 注 在 upload 函数中 使用 flask request files KEY 获取上传文件对象 KEY 为页面 form 中 input 的 name 值 因为是在后台输出内容 所以测试最好拿纯文本文件来测 保存到 mongodb 如果不那么讲究的话 最快速基本的存储方案里只需要 import pymongo import bson binary from cStringIO import StringIO app flask Flask name app debug True db pymongo MongoClient localhost 27017 test def save file f content StringIO f read db files save dict content bson binary Binary content getvalue app route upload methods POST def upload f flask request files uploaded file save file f return flask redirect 把内容塞进一个 bson binary Binary 对象 再把它扔进 mongodb 就可以了 现在试试再上传个什么文件 在 mongo shell 中通过 db files find 就能看到了 不过 content 这个域几乎肉眼无法分辨出什么东西 即使是纯文本文件 mongo 也会显示为 Base64 编码 提供文件访问 给定存进数据库的文件的 ID 作为 URI 的一部分 返回给浏览器其文件内容 如下 def save file f content StringIO f read c dict content bson binary Binary content getvalue db files save c return c id app route f fid def serve file fid f db files find one bson objectid ObjectId fid return f content app route upload methods POST def upload f flask request files uploaded file fid save file f return flask redirect f str fid 上传文件之后 upload 函数会跳转到对应的文件浏览页 这样一来 文本文件内容就可以正常预览了 如果不是那么挑剔换行符跟连续空格都被浏览器吃掉的话 当找不到文件时 有两种情况 其一 数据库 ID 格式就不对 这时 pymongo 会抛异常 bson errors InvalidId 其二 找不到对象 这时 pymongo 会返回 None 简单起见就这样处理了 app route f fid def serve file fid import bson errors try f db files find one bson objectid ObjectId fid if f is None raise bson errors InvalidId return f content except bson errors InvalidId flask abort 404 正确的 MIME 从现在开始要对上传的文件严格把关了 文本文件 狗与剪刀等皆不能上传 判断图片文件之前说了我们动真格用 Pillow from PIL import Image allow formats set jpeg png gif def save file f content StringIO f read try mime Image open content format lower if mime not in allow formats raise IOError except IOError flask abort 400 c dict content bson binary Binary content getvalue db files save c return c id 然后试试上传文本文件肯定虚 传图片文件才能正常进行 不对 也不正常 因为传完跳转之后 服务器并没有给出正确的 mimetype 所以仍然以预览文本的方式预览了一坨二进制乱码 要解决这个问题 得把 MIME 一并存到数据库里面去 并且 在给出文件时也正确地传输 mimetype def save file f content StringIO f read try mime Image open content format lower if mime not in allow formats raise IOError except IOError flask abort 400 c dict content bson binary Binary content getvalue mime mime db files

    Original URL path: http://rss.bitfoc.us/ (2016-04-27)
    Open archived version from archive




  •