

<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Dropboxlike開發筆記 &#8211; Max的程式語言筆記</title>
	<atom:link href="https://stackoverflow.max-everyday.com/category/dropboxlike/feed/" rel="self" type="application/rss+xml" />
	<link>https://stackoverflow.max-everyday.com</link>
	<description>我要當一個豬頭，快樂過每一天</description>
	<lastBuildDate>Wed, 03 Jan 2024 11:13:17 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>

<image>
	<url>https://stackoverflow.max-everyday.com/wp-content/uploads/2017/02/max-stackoverflow-256.png</url>
	<title>Dropboxlike開發筆記 &#8211; Max的程式語言筆記</title>
	<link>https://stackoverflow.max-everyday.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>在tornado做出網址即為資料的id的handler</title>
		<link>https://stackoverflow.max-everyday.com/2023/02/tornado-id-handler/</link>
					<comments>https://stackoverflow.max-everyday.com/2023/02/tornado-id-handler/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 22 Feb 2023 09:05:36 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=4383</guid>

					<description><![CDATA[這個需求只用一句話講滿抽象的, 以觀看還有編輯 ...]]></description>
										<content:encoded><![CDATA[
<p>這個需求只用一句話講滿抽象的, 以觀看還有編輯 youtube 影片為例, 影片的 id 就在網址裡. </p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="525" height="208" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-22-17_00_47-影片詳細資料-YouTube-Studio.jpg" alt="" class="wp-image-4385"/></figure>



<p></p>



<p>也可以參考看看 tornado 的 blog 範例, 他的  router 下法:</p>



<pre class="wp-block-code"><code>class Application(tornado.web.Application):
    def __init__(self, db):
        self.db = db
        handlers = &#91;
            (r"/", HomeHandler),
            (r"/archive", ArchiveHandler),
            (r"/feed", FeedHandler),
            (r"/entry/(&#91;^/]+)", EntryHandler),
            (r"/compose", ComposeHandler),
            (r"/auth/create", AuthCreateHandler),
            (r"/auth/login", AuthLoginHandler),
            (r"/auth/logout", AuthLogoutHandler),
        ]
</code></pre>



<p></p>



<p>在 EntryHandler 裡, 存取 slug 就可以拿到 url 裡的我們要的 id.</p>



<pre class="wp-block-code"><code>class EntryHandler(BaseHandler):
    async def get(self, slug):
        entry = await self.queryone("SELECT * FROM entries WHERE slug = %s", slug)
        if not entry:
            raise tornado.web.HTTPError(404)
        self.render("entry.html", entry=entry)
</code></pre>



<p></p>



<p>如果確定是拿 /&#8230;/edit 的, 也可以設定成這樣子:</p>



<pre class="wp-block-code"><code>(r'/your-object/(.*)/edit', 'app.controller.ObjectEditorHandler'),</code></pre>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2023/02/tornado-id-handler/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tornado 也是最好用的輕量級 web server</title>
		<link>https://stackoverflow.max-everyday.com/2023/02/404-while-using-tornado-staticfilehandler-class/</link>
					<comments>https://stackoverflow.max-everyday.com/2023/02/404-while-using-tornado-staticfilehandler-class/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Fri, 17 Feb 2023 07:12:12 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=4350</guid>

					<description><![CDATA[跟 Flask 相比, tornado 似乎討論...]]></description>
										<content:encoded><![CDATA[
<p>跟 Flask 相比, tornado 似乎討論的文章比較少, 但Max最喜歡使用的 web server 是 tornado, 今天來介紹 tornado 如何與 Flask 一樣, 掛載靜態檔案. </p>



<p>Flash 的使用方法, 真的很直覺, 程式碼也超短:<br><a href="https://stackoverflow.max-everyday.com/2023/02/flask-web-server-static-file/">https://stackoverflow.max-everyday.com/2023/02/flask-web-server-static-file/</a></p>



<p>Tornado 對靜態檔案的說明:<br><a href="https://www.tornadoweb.org/en/stable/web.html#tornado.web.StaticFileHandler">https://www.tornadoweb.org/en/stable/web.html#tornado.web.StaticFileHandler</a></p>



<p>使用下面的範例, 就可以存取到靜態檔案:</p>



<pre class="wp-block-code"><code>import asyncio
import tornado
from tornado.web import Application
from tornado.web import StaticFileHandler

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return Application(&#91;
        (r"/", MainHandler),
        (r"/content/(.*)", StaticFileHandler, {"path": "./content"}),
    ])

async def main():
    app = make_app()
    app.listen(8888)
    await asyncio.Event().wait()

if __name__ == "__main__":
    asyncio.run(main())</code></pre>



<p>說明: ./content/ 輸出網頁的 /content/</p>



<p>使用的執行範例:<br>http://127.0.0.1:8888/content/static.py</p>



<figure class="wp-block-image size-full"><img decoding="async" width="558" height="444" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-17-14_49_15-127.0.0.1_8888_content_static.py_.jpg" alt="" class="wp-image-4351"/></figure>



<p></p>



<p>有一個奇怪的坑是, 如果你在 create Application(routes, **app_settings) object 時, 所帶入的 app_settings = dict( ) 中間使用 static_path, 例如:</p>



<pre class="wp-block-code"><code>app_settings = dict(
    template_path=os.path.join(os.path.dirname(__file__), "templates"),
    static_path=os.path.join(os.path.dirname(__file__), "static"),</code></pre>



<p>這時候, 又在 routes 裡設了 static, 例如:</p>



<pre class="wp-block-code"><code>handlers = &#91;
        (r'/', MainHandler),
        (r'/static/(.*)', StaticFileHandler, {'path': "static"}),
    ]</code></pre>



<p>2邊就會衝突, 原本應該正常顯示的靜態檔案都會成錯誤訊息:</p>



<pre class="wp-block-code"><code>Traceback (most recent call last):
  File "D:\Programs\Python\Python39-32\lib\site-packages\tornado\web.py", line 1713, in _execute
    result = await result
  File "D:\Programs\Python\Python39-32\lib\site-packages\tornado\web.py", line 2611, in get
    self.absolute_path = self.validate_absolute_path(self.root, absolute_path)
  File "D:\Programs\Python\Python39-32\lib\site-packages\tornado\web.py", line 2813, in validate_absolute_path
    raise HTTPError(404)
tornado.web.HTTPError: HTTP 404: Not Found</code></pre>



<figure class="wp-block-image size-full"><img decoding="async" width="817" height="229" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-17-15_08_45-https___127.0.0.1_5000_static_components.json_.jpg" alt="" class="wp-image-4352" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-17-15_08_45-https___127.0.0.1_5000_static_components.json_.jpg?v=1676617750 817w, https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-17-15_08_45-https___127.0.0.1_5000_static_components.json_-600x168.jpg?v=1676617750 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-17-15_08_45-https___127.0.0.1_5000_static_components.json_-768x215.jpg?v=1676617750 768w" sizes="(max-width: 817px) 100vw, 817px" /></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>根目錄以下到 /static/目錄下取檔案, 並針對 404 不存在的檔案做客製化:</p>



<pre class="wp-block-code"><code>class MyStaticFileHandler(StaticFileHandler):
    def write_error(self, status_code, *args, **kwargs):
        my_404_page = os.path.join(options.app_root, 'template/error_404.html')
        self.set_status(status_code)
        if status_code in &#91;404]:
            self.render(my_404_page)
        else:
            self.write("Error: %d" % status_code)

handlers = &#91;
    (r'/', 'app.controller.HomeHandler'),
    (r'/(.*)', MyStaticFileHandler, {'path': os.path.join(app_root, 'static/')}),
]</code></pre>



<p> </p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2023/02/404-while-using-tornado-staticfilehandler-class/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Ubuntu Server 安裝 Postfix + pop3 imap</title>
		<link>https://stackoverflow.max-everyday.com/2018/03/ubuntu-server-postfix-pop3-imap/</link>
					<comments>https://stackoverflow.max-everyday.com/2018/03/ubuntu-server-postfix-pop3-imap/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Fri, 16 Mar 2018 10:46:45 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[WordPress筆記]]></category>
		<category><![CDATA[電腦相關應用]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=2141</guid>

					<description><![CDATA[今天會學的技能是在Linux 上架設mail s...]]></description>
										<content:encoded><![CDATA[
<p>今天會學的技能是在Linux 上架設mail server，Postfix其實沒有想像中的複雜，基本的設定值也很簡單，比較難的是「寄信/收信遇到問題」的處理。</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="788" height="523" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2018/03/Screenshot-2018-03-16-14.56.58.jpg" alt="" class="wp-image-2143" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2018/03/Screenshot-2018-03-16-14.56.58.jpg 788w, https://stackoverflow.max-everyday.com/wp-content/uploads/2018/03/Screenshot-2018-03-16-14.56.58-600x398.jpg 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2018/03/Screenshot-2018-03-16-14.56.58-768x510.jpg 768w" sizes="(max-width: 788px) 100vw, 788px" /></figure>



<p>你一定覺得很奇怪，gmail 和 yahoo 信箱就很好用了，為何要自己去架設？因為facebook Workspace 只能使用公司的 Email。</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="496" height="602" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2018/03/Screenshot-2018-03-16-18.54.28.jpg" alt="" class="wp-image-2147" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2018/03/Screenshot-2018-03-16-18.54.28.jpg 496w, https://stackoverflow.max-everyday.com/wp-content/uploads/2018/03/Screenshot-2018-03-16-18.54.28-494x600.jpg 494w" sizes="(max-width: 496px) 100vw, 496px" /></figure>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>Facebook Workplace 可以將同事連結在一起，方便大家交流意見，免費進行群組對話、傳送訊息、撥打語音／視訊電話，並享有無限儲存空間。專為企業設計的實用工具。</p></blockquote>



<p>不自己架的話，可以直接花錢買gmail for bussiness，如果想省錢就花點時間自己架了。</p>



<hr class="wp-block-separator"/>



<h2 class="wp-block-heading">安裝用指令：</h2>



<pre class="wp-block-preformatted">sudo apt-get install postfix</pre>



<p>啟動停止 Postfix 的指令如下：</p>



<pre class="wp-block-preformatted">sudo /etc/init.d/postfix start #啟動 Postfix
sudo /etc/init.d/postfix stop #停止 Postfix
sudo /etc/init.d/postfix restart #重新啟動 Postfix</pre>



<p>Postfix 主要的組態設定檔是 /etc/postfix/main.cf。<br>編輯 main.cf 的指令如下：</p>



<pre class="wp-block-preformatted">sudo vi /etc/postfix/main.cf</pre>



<hr class="wp-block-separator"/>



<p>Postfix一些常用的指令：</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p># 列出目前在 Mail Queue 中的信件<br>mailq<br><br># 刪除所有在 Queue 中的郵件<br>postsuper&nbsp;-d ALL<br><br># 刪除所有正在 deferred 佇列中的郵件 ( 刪除曾經發送失敗的信 )<br>postsuper -d ALL deferred<br><br># 刪除所有正在 deferred 佇列中的郵件 ( 可看出哪些信被刪除了 )<br>find /var/spool/postfix/deferred -type f -exec rm -vf \{\} \;</p><p># 刪掉「三天以前」無法發出的郵件<br>find /var/spool/postfix/deferred -type f -mtime +3 -exec rm -f \{\} \;</p><p># 列出目前所有無法發出的郵件<br>find /var/spool/postfix/deferred -type f -exec ls -l &#8211;time-style=+%Y-%m-%d_%H:%M:%S {} \;<br><br># 刪除超過 5 天的 &#8220;defer&#8221; 佇列中的退信紀錄<br>find /var/spool/postfix/defer -type f -mtime +5 -exec rm -f \{\} \;</p></blockquote>



<hr class="wp-block-separator"/>



<p>檢查為什麼無法收到信：</p>



<p>vi /var/log/mail.log</p>



<p>進入 vi 後，按大寫G，移到 end of file，訊息：</p>



<pre class="wp-block-preformatted">postfix/smtp[6063]: DDA94BD4E4: to=<a href="mailto:weng.32002@gmail.com">weng.32002@gmail.com</a>, relay=gmail-smtp-in.l.google.com[74.125.24.26]:25, delay=0.88, delays=0.06/0.02/0.34/0.46, dsn=5.7.28, status=bounced (host gmail-smtp-in.l.google.com[74.125.24.26] said: 550-5.7.28 [167.172.78.3       1] Our system has detected an unusual rate of 550-5.7.28 unsolicited mail originating from your IP address. To protect our 550-5.7.28 users from spam, mail sent from your IP address has been blocked. 550-5.7.28 Please visit 550-5.7.28  https://support.google.com/mail/?p=UnsolicitedIPError to review our 550 5.7.28 Bulk Email Senders Guidelines. 31si2288714ple.177 - gsmtp (in reply to end of DATA command))</pre>



<hr class="wp-block-separator"/>



<p>訊息被Google 擋掉。</p>



<p>解法，增加 TXT 到 DNS，DNS 提供網域名稱與IP位址對應的查詢，除了基本的&nbsp;A、MX、CNAME、PTR、SRV 記錄外，還有一個 TXT 記錄，他可提供文字資訊給本身網域以外的來源一些額外的資訊，例如 SPF (&nbsp;Sender Policy Framework )。</p>



<p>SPF 到底可以拿來幹嘛？</p>



<p>SPF 可以讓 收信端主機 根據來信中的 寄件者郵件網域，主動去寄信端所屬的DNS核對SPF記錄，當寄件者郵件網域 不同/不合法 於寄信主機網域時，可以丟棄、退信、或特別標註該封郵件。SPF 可以阻擋別人冒用你的網域寄信。</p>



<p>使用 dig 指令查詢網域的SPF設定，查詢資料類型請使用TXT。</p>



<pre class="wp-block-preformatted">dig @8.8.8.8 dropboxlike.com TXT</pre>



<hr class="wp-block-separator"/>



<p>GMail 對被退信的說明：<br><a href="https://support.google.com/mail/answer/81126?p=UnsolicitedIPError&amp;visit_id=637161508037219534-3989246197&amp;rd=1">https://support.google.com/mail/answer/81126?p=UnsolicitedIPError&amp;visit_id=637161508037219534-3989246197&amp;rd=1</a></p>



<p>為了盡可能減低郵件遭標示為垃圾郵件的機率，請設定以下驗證方式：</p>



<ul class="wp-block-list"><li>為網域發布&nbsp;<a href="https://en.wikipedia.org/wiki/Sender_Policy_Framework#Implementation" target="_blank" rel="noreferrer noopener">SPF 記錄</a>。SPF 可防止垃圾郵件發布者假借您網域的名義寄出未經授權的郵件。</li><li>為郵件<a href="http://www.dkim.org/" target="_blank" rel="noreferrer noopener">開啟 DKIM 簽署功能</a>。收件伺服器會透過 DKIM 驗證網域擁有者是否確實傳送了郵件。重要資訊：Gmail 規定使用 1024 位元以上的 DKIM 金鑰。</li><li>為網域發布&nbsp;<a href="https://dmarc.org/" target="_blank" rel="noreferrer noopener">DMARC 記錄</a>。DMARC 可協助寄件者防止自己網域的電子郵件遭到假冒。</li></ul>



<p>為了讓 SPF 和 DKIM 順利驗證郵件，郵件的「From:」標頭必須與寄件網域相符。郵件則須通過 SPF 或 DKIM 檢查才算完成驗證。</p>



<p>請勿在未經授權的情況下冒用其他網域或寄件者名義傳送郵件。這種做法稱為「假冒」，可能會導致 Gmail 將郵件歸類為垃圾郵件。</p>



<p>設好 SPF 後新的 mail.log:</p>



<pre class="wp-block-preformatted">postfix/smtp[6598]: 54723BD202: to=&lt;do-no-reply@moca-shop.com&gt;, relay=eforward3.registrar-servers.com[162.255.118.51]:25, delay=1, delays=0/0/0.82/0.2, dsn=5.1.1, status=bounced (host eforward3.registrar-servers.com[162.255.118.51] said: 554 5.1.1 &lt;do-no-reply@moca-shop.com&gt;: Recipient address rejected: undeliverable address: host mail-outgoing-fef.relay.svc.cluster.local[10.27.5.108] said: 554 5.7.1 &lt;do-no-reply@moca-shop.com&gt;: Relay access denied (in reply to RCPT TO command) (in reply to RCPT TO command))</pre>



<hr class="wp-block-separator"/>



<p>解法：</p>



<p>因為DNS裡太多 MX  的欄位，刪一刪，只留下一組即可。</p>



<hr class="wp-block-separator"/>



<p>也可以使用 CheckTLS.com 的測試功能幫忙測試一下，填上主機上可收信的信箱，按下 Start Test 即可：<br><a href="https://www.checktls.com/TestReceiver">https://www.checktls.com/TestReceiver</a></p>



<hr class="wp-block-separator"/>



<p>如果 postfix 回報 Relay access denied;</p>



<p>那是因為 /etc/postfix/main.cf 的設定值：</p>



<pre class="wp-block-preformatted">smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination</pre>



<p>所以，如果不是從本機或內網或非授權的 domain name 那就會被拒絕，顯示 Relay access denied。</p>



<p>寄到Gmail 的信容易變「垃圾信」，所以一定要幫信建立規則（篩選器）：</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="857" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-05-at-00.52.10.jpg" alt="" class="wp-image-3192" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-05-at-00.52.10.jpg?v=1580835492 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-05-at-00.52.10-600x502.jpg?v=1580835492 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-05-at-00.52.10-768x643.jpg?v=1580835492 768w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<hr class="wp-block-separator"/>



<p>最後，如果你真的問題實在太多，真心建議改用Gmail API 來寄送：</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="779" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-01-at-19.48.04.jpg" alt="" class="wp-image-3174" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-01-at-19.48.04.jpg?v=1580557787 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-01-at-19.48.04-600x456.jpg?v=1580557787 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-01-at-19.48.04-768x584.jpg?v=1580557787 768w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<hr class="wp-block-separator"/>



<h2 class="wp-block-heading">安裝教學：</h2>



<p>《分享》Ubuntu架設mail server<br><a href="http://www.shunze.info/forum/thread.php?boardid=3&amp;threadid=2015">http://www.shunze.info/forum/thread.php?boardid=3&amp;threadid=2015</a></p>



<p>使用 Ubuntu 安裝郵件伺服器 (Mail Server)：Postfix + Dovecot + Openwebmail<br><a href="http://jensoncc.blogspot.tw/2012/08/ubuntu-mail-serverpostfix-dovecot.html">http://jensoncc.blogspot.tw/2012/08/ubuntu-mail-serverpostfix-dovecot.html</a></p>



<p>Ubuntu Server 安裝 Postfix + pop3 imap<br><a href="http://linadonis.pixnet.net/blog/post/7340997">http://linadonis.pixnet.net/blog/post/7340997</a></p>



<hr class="wp-block-separator"/>



<p>外國人寫的也滿好的，看英文吃力很多，max心得是：很多地方看不懂就跳過就好了，重點是紅字的部份都要看懂即可：</p>



<p>Mark as Complete<br>How To Install and Configure Postfix on Ubuntu 16.04<br><a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-postfix-on-ubuntu-16-04">https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-postfix-on-ubuntu-16-04</a></p>



<p>How To Set Up a Postfix E-Mail Server with Dovecot<br><a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-postfix-e-mail-server-with-dovecot">https://www.digitalocean.com/community/tutorials/how-to-set-up-a-postfix-e-mail-server-with-dovecot</a></p>



<p>How to Install and Configure Postfix as a Send-Only SMTP Server on Ubuntu 16.04<br><a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-postfix-as-a-send-only-smtp-server-on-ubuntu-16-04">https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-postfix-as-a-send-only-smtp-server-on-ubuntu-16-04</a></p>



<p>Setup mail forwarding in postfix on Ubuntu or Debian<br><a href="https://www.binarytides.com/postfix-mail-forwarding-debian/">https://www.binarytides.com/postfix-mail-forwarding-debian/</a></p>



<hr class="wp-block-separator"/>



<p>連線加密，可以用 let encrypt:</p>



<p>Using let’s encrypt certs with postfix<br><a href="https://stackoverflow.max-everyday.com/2017/02/handling-exceptions-tornado/">https://stackoverflow.max-everyday.com/2017/02/handling-exceptions-tornado/</a></p>



<p>letsencrypt 的 certbot 的官方說明：<br><a href="https://certbot.eff.org/#ubuntuxenial-other">https://certbot.eff.org/#ubuntuxenial-other</a></p>



<p>certbot 指令用法：</p>



<pre class="wp-block-preformatted">certbot --help</pre>



<p>新的版本的 certbot 用起來更簡單：</p>



<pre class="wp-block-preformatted">certbot certonly --cert-name your-domain-name</pre>



<p>Max 是使用手動模式：</p>



<pre class="wp-block-preformatted">certbot certonly --manual -d mail.dropboxlike.com</pre>



<p>畫面顯示：</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="477" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-01-at-17.16.49.jpg" alt="" class="wp-image-3169" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-01-at-17.16.49.jpg?v=1580548697 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-01-at-17.16.49-600x279.jpg?v=1580548697 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-01-at-17.16.49-768x358.jpg?v=1580548697 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2020/02/Screen-Shot-2020-02-01-at-17.16.49-750x350.jpg?v=1580548697 750w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>可以另外再開一個 terminal 連進去伺服器裡，先使用 curl http://mail.dropboxlike.com/ 來看看有沒有顯示 certbot 所需要的內容。</p>



<p>Max 的指令：</p>



<pre class="wp-block-preformatted"> ➜  html cd .well-known 
 ➜  .well-known mkdir acme-challenge
 ➜  .well-known cd acme-challenge 
 ➜  acme-challenge echo "KECpMIVxKY_-bWY_gGY3Hqqwvj-q_Pi0wy0eJdvPNsw.wxfS4hcTbJJfl1qAmLveBUuXTdcQeGE04Mvfw4Ed1NA" &gt; KECpMIVxKY_-bWY_gGY3Hqqwvj-q_Pi0wy0eJdvPNsw</pre>



<p>透過 echo 把內容放進檔案裡後，再回到 certbot 按下 Enter</p>



<pre class="wp-block-preformatted"> Press Enter to Continue
 Waiting for verification…
 Cleaning up challenges
 IMPORTANT NOTES:
 Congratulations! Your certificate and chain have been saved at:
 /etc/letsencrypt/live/mail.dropboxlike.com/fullchain.pem
 Your key file has been saved at:
 /etc/letsencrypt/live/mail.dropboxlike.com/privkey.pem
 Your cert will expire on 2020-05-01. To obtain a new or tweaked
 version of this certificate in the future, simply run certbot
 again. To non-interactively renew <em>all</em> of your certificates, run
 "certbot renew"
 If you like Certbot, please consider supporting our work by:
 Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 Donating to EFF:                    https://eff.org/donate-le </pre>



<p>就成功拿到憑證了. ^_^</p>



<hr class="wp-block-separator"/>



<p>Max的參數設定 /etc/postfix/main.cf</p>



<pre class="wp-block-preformatted"># TLS parameters
smtpd_tls_cert_file=/etc/letsencrypt/live/mail.dropboxlike.com/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/mail.dropboxlike.com/privkey.pem
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

smtp_use_tls = yes
smtp_tls_note_starttls_offer = yes</pre>



<p>上面紅字部份是我有修改的。</p>



<p>將 Let’s Encrypt 申請到的 SSL 憑證用在 Postfix 上，提供 SMTP with TLS 和 smtps 服務<br><a href="https://ezbox.idv.tw/63/lets-encrypt-ssl-smtp-tls-smpts/">https://ezbox.idv.tw/63/lets-encrypt-ssl-smtp-tls-smpts/</a></p>



<hr class="wp-block-separator"/>



<p>如果有其他 user 也要用，可以加入下面這行設定值：</p>



<pre class="wp-block-preformatted">local_recipient_maps = proxy:unix:passwd.byname $alias_maps</pre>



<p>完整的設定值，可以參考 /etc/postfix/main.cf.proto</p>



<p>除了設定&nbsp; postfix 還要去設&nbsp; dovecot 打開 SSL 選項，</p>



<pre class="wp-block-preformatted"># nano /etc/dovecot/conf.d/10-ssl.conf</pre>



<p>找到 ssl 這行，把 no 改成 yes</p>



<pre class="wp-block-preformatted">ssl = yes</pre>



<p>並且把自己的私鑰和公鑰指向檔案的路徑</p>



<pre class="wp-block-preformatted">ssl_key =&lt;/etc/letsencrypt/live/mail.dropboxlike.com/privkey.pem
ssl_cert =&lt;/etc/letsencrypt/live/mail.dropboxlike.com/fullchain.pem</pre>



<hr class="wp-block-separator"/>



<p>mail server debug 用的 log:</p>



<p>/var/log/mail.log</p>



<hr class="wp-block-separator"/>



<p>必學的 2個指令：</p>



<pre class="wp-block-preformatted">dig&nbsp; your_domain

host your_domain</pre>



<p>一定要確定 DNS 相關設定值是正確的。</p>



<p>如果你遇到任何的錯誤，請先確定連線的 ip 有對應到你指定的 server</p>



<hr class="wp-block-separator"/>



<p>遇到：How to correct Postfix&#8217; &#8216;Relay Access Denied&#8217;? 處理</p>



<pre class="wp-block-preformatted">Diagnostic-Code: smtp; 554 5.7.1 &lt;max@gmail.com&gt;: Relay access denied</pre>



<p>you now need to authorise your local network by adding it to&nbsp;<code>mynetworks</code>. For example,</p>



<pre class="wp-block-code"><code>mynetworks = 192.168.1.0/24 127.0.0.0/8</code></pre>



<hr class="wp-block-separator"/>



<p>測試 POP3</p>



<pre class="wp-block-preformatted">telnet mail.dropboxmax.com 110
Trying 128.199.124.123...
Connected to mail.dropboxmax.com.
Escape character is '^]'.
+OK Dovecot ready.
USER max
-ERR [AUTH] Plaintext authentication disallowed on non-secure (SSL/TLS) connections.</pre>



<hr class="wp-block-separator"/>



<p>centos dsn client 的設定值放在 /etc/resolv.conf 檔案裡，設定範例：</p>



<pre class="wp-block-preformatted">nameserver 8.8.8.8</pre>



<p>上面的做法，在ubuntu 裡已過時，需要改用這個解法：</p>



<p>在 /etc/network/interfaces 檔裡，加入下面這一行就可以指定 DNS Server 了</p>



<pre class="wp-block-preformatted">dns-nameservers 8.8.8.8 168.95.192.1</pre>



<p>滿奇怪的，這個設定檔好像需要內縮，沒有內縮，我使用 reboot 後，dns 設定值沒有被修改，內縮後reboot 即可取到正確的 DNS 設定。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2018/03/ubuntu-server-postfix-pop3-imap/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>socket.gaierror: [Errno 8] nodename nor servname provided, or not known</title>
		<link>https://stackoverflow.max-everyday.com/2017/09/socket-gaierror-errno-8-nodename-nor-servname-provided-or-not-known/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/09/socket-gaierror-errno-8-nodename-nor-servname-provided-or-not-known/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Mon, 04 Sep 2017 03:15:50 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Debug]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=1195</guid>

					<description><![CDATA[當執行下面的 script 會出錯： impor...]]></description>
										<content:encoded><![CDATA[<p>當執行下面的 script 會出錯：</p>
<pre>import socket
  (hostname, aliaslist, ipaddrlist) = socket.gethostbyname_ex(socket.gethostname())</pre>
<hr />
<p>Error message:</p>
<pre>socket.gaierror: [Errno 8] nodename nor servname provided, or not known</pre>
<hr />
<p>真的滿奇怪的，只好改從 netifaces 來拿 ip</p>
<p>python 取得 ip address 的方法<br />
<a href="https://stackoverflow.max-everyday.com/2017/02/python-netifaces/">https://stackoverflow.max-everyday.com/2017/02/python-netifaces/</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/09/socket-gaierror-errno-8-nodename-nor-servname-provided-or-not-known/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Java三行代码搞定MD5加密</title>
		<link>https://stackoverflow.max-everyday.com/2017/08/get-md5-hash-in-a-few-lines-of-java/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/08/get-md5-hash-in-a-few-lines-of-java/#comments</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Thu, 17 Aug 2017 04:03:53 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Java筆記]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[password]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=1108</guid>

					<description><![CDATA[Get MD5 Hash In A Few Li...]]></description>
										<content:encoded><![CDATA[<p>Get MD5 Hash In A Few Lines Of Java</p>
<pre> /**
 * 對字符串md5加密
 *
 * @param str
 * @return
 */
 public String getMD5(String str) {
 String ret = null;
 try {
 // 生成一個MD5加密計算摘要
 MessageDigest md = MessageDigest.getInstance("MD5");
 // 計算md5函數
 md.update(str.getBytes());
 // digest()最後確定返回md5 hash值，返回值為8為字符串。因為md5 hash值是16位的hex值，實際上就是8位的字符
 // BigInteger函數則將8位的字符串轉換成16位hex值，用字符串來表示；得到字符串形式的hash值
 ret = new BigInteger(1, md.digest()).toString(16);
 } catch (Exception e) {
 //throw new SpeedException("MD5加密出現錯誤");
 e.printStackTrace();
 }
 return ret;
 }</pre>
<p> </p>


<hr class="wp-block-separator"/>



<p>java.math.BigInteger.toString(int radix)方法實例</p>



<p><strong>java.math.BigInteger.toString(int radix) </strong>返回此BigInteger在給定的基數的字符串表示形式。如果基數是從Character.MIN_RADIX到Character.MAX_RADIX包容的範圍內，它會默認為10(因為Integer.toString的情況下)。</p>



<p><strong>聲明</strong></p>



<p>以下是java.math.BigInteger.toString()方法的聲明</p>



<p>public String toString(int radix)</p>



<p><strong>參數</strong></p>



<p>radix &#8211; 該字符串表示形式的基數</p>



<p><strong>返回值</strong></p>



<p>此方法返回此BigInteger在給定的基數的字符串表示形式。</p>



<hr class="wp-block-separator"/>



<p>BigInteger(byte[] val)<br>BigInteger的大小为val的顺序拼接结果</p>



<pre class="wp-block-preformatted">byte[] val = new byte[]{0x11,0x22,0x33};<br>BigInteger bigInteger = new BigInteger(val);<br>System.out.println(bigInteger.toString(16)); //16进制输出 , 结果 : 112233</pre>



<hr class="wp-block-separator"/>



<p>BigInteger(String val, int radix)<br>得到大小为val , 以 radix 为基数的 BigInteger</p>



<pre class="wp-block-preformatted">BigInteger bigInteger = new BigInteger("1F", 16);<br>System.out.println(bigInteger.toString(16));//输出结果 1f<br>System.out.println(bigInteger.toString(10));//输出结果 31</pre>



<hr class="wp-block-separator"/>



<pre class="wp-block-preformatted">String s1 = "126656864e144ad88d7ff96badd2f68b"; // 16进制数
BigInteger b = new BigInteger(s1,16);           // 16进制转成大数类型    
String s2 = b.toString(16);                     // 大数类型转成16进制</pre>



<p>上列的範例輸入結果：</p>



<pre class="wp-block-preformatted">s1: 126656864e144ad88d7ff96badd2f68b<br>s2: 126656864e144ad88d7ff96badd2f68b</pre>



<p>如果前2碼修改為00，則：</p>



<pre class="wp-block-preformatted">s1: 006656864e144ad88d7ff96badd2f68b<br>s2: 6656864e144ad88d7ff96badd2f68b</pre>



<p>所以，最上面的例子，前面要補滿0 才不會出錯。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/08/get-md5-hash-in-a-few-lines-of-java/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Mysql備份與還原資料庫</title>
		<link>https://stackoverflow.max-everyday.com/2017/04/mysql-dump-restore/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/04/mysql-dump-restore/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Mon, 03 Apr 2017 04:01:21 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Dropboxlike]]></category>
		<category><![CDATA[mysql]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=402</guid>

					<description><![CDATA[原來 mysql 的備份還有還原這麼簡單 @_@...]]></description>
										<content:encoded><![CDATA[
<p>原來 mysql 的備份還有還原這麼簡單 @_@；</p>



<h2 class="wp-block-heading">備份：</h2>



<p>&#8212; 備份某個資料庫</p>



<pre class="wp-block-preformatted">mysqldump -u root -p db_name &gt; backup.sql;</pre>



<p>&#8212; 備份所有資料庫</p>



<pre class="wp-block-preformatted">mysqldump -u root -p --all-databases &gt; backup.sql;</pre>



<p>附註：備所有資料庫，遇到 database 版本不同時，會有問題，例如 mysql 5.6 到 5.7 會造成系統預設的欄位長度不符的錯誤。</p>



<p>備份用的 shell script:</p>



<pre class="wp-block-preformatted">#!/bin/sh 
pw='your-password'
mysqldump --all-databases --add-drop-table -h 127.0.0.1 -u root -p$pw &gt; your-backup-file.sql
bzip2 -f your-backup-file.sql</pre>



<hr class="wp-block-separator"/>



<p>bz2 壓縮：</p>



<p>bzip2 -z FileName</p>



<p>bz2 解壓縮：<br>bzip2 -d FileName.bz2</p>



<hr class="wp-block-separator"/>



<h2 class="wp-block-heading">還原：</h2>



<pre class="wp-block-preformatted">mysql -u root -p &lt; backup.sql</pre>



<p>如果出現 no database selected 是因為 backup.sql 裡沒有包括了 create database 的指令，只有去 drop table. 所以要在一還原的地方指定好要被貼回來的 database name:</p>



<pre class="wp-block-preformatted">mysql DATABASE_NAME -u root -p &lt; backup.sql</pre>



<p>進階一點的備份：</p>



<p>MySQL 自動備份 Shell Script<br><a href="https://stackoverflow.max-everyday.com/2018/01/mysql-shell-script/">https://stackoverflow.max-everyday.com/2018/01/mysql-shell-script/</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/04/mysql-dump-restore/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Stream a file with tornado</title>
		<link>https://stackoverflow.max-everyday.com/2017/03/stream-a-file-with-tornado/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/03/stream-a-file-with-tornado/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 28 Mar 2017 01:32:14 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Dropboxlike]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=397</guid>

					<description><![CDATA[現在手機上的空間往往不夠，所以影片最好就是直接連...]]></description>
										<content:encoded><![CDATA[<p>現在手機上的空間往往不夠，所以影片最好就是直接連到server 上透過串流回來看就好了，在tornado 上串流的 demo:</p>
<p><a href="https://github.com/tornadoweb/tornado/issues/1046">https://github.com/tornadoweb/tornado/issues/1046</a></p>
<p>完整版Source code:<br />
<a href="http://tornado-doc-chs.readthedocs.io/en/latest/_modules/tornado/web.html">http://tornado-doc-chs.readthedocs.io/en/latest/_modules/tornado/web.html</a></p>
<p><span class="k">class</span> <span class="nc">StaticFileHandler</span><span class="p">(</span><span class="n">RequestHandler</span><span class="p">):</span></p>
<hr />
<p>Alright this time I found the actual solution:</p>
<div class="highlight highlight-source-python">
<pre>            <span class="pl-c"># Note: only return HTTP 206 if less than the entire range has been</span>
            <span class="pl-c"># requested. Not only is this semantically correct, but Chrome</span>
            <span class="pl-c"># refuses to play audio if it gets an HTTP 206 in response to</span>
            <span class="pl-c"># ``Range: bytes=0-``.</span></pre>
</div>
<p>This note applies to audio only, chrome will play video that begins be returning a 206.</p>
<div class="highlight highlight-source-python">
<pre>    <span class="pl-en">@asynchronous</span>
    <span class="pl-en">@gen.coroutine</span>
    <span class="pl-k">def</span> <span class="pl-en">get</span>(<span class="pl-smi">self</span>, <span class="pl-smi">path</span>, <span class="pl-smi">include_body</span><span class="pl-k">=</span><span class="pl-c1">True</span>):
        <span class="pl-c">#Assume that path is correct, validation will be handeled elsewhere</span>
        <span class="pl-c1">self</span>.absolute_path <span class="pl-k">=</span> os.path.abspath(path)
        <span class="pl-c1">self</span>.path <span class="pl-k">=</span> <span class="pl-c1">self</span>.absolute_path
        <span class="pl-k">if</span> <span class="pl-c1">self</span>.absolute_path <span class="pl-k">is</span> <span class="pl-c1">None</span>:
            <span class="pl-k">return</span>

        <span class="pl-c1">self</span>.modified <span class="pl-k">=</span> <span class="pl-c1">self</span>.get_modified_time()
        <span class="pl-c1">self</span>.set_headers()

        <span class="pl-k">if</span> <span class="pl-c1">self</span>.should_return_304():
            <span class="pl-c1">self</span>.set_status(<span class="pl-c1">304</span>)
            <span class="pl-k">return</span>

        request_range <span class="pl-k">=</span> <span class="pl-c1">None</span>
        range_header <span class="pl-k">=</span> <span class="pl-c1">self</span>.request.headers.get(<span class="pl-s"><span class="pl-pds">"</span>Range<span class="pl-pds">"</span></span>)
        <span class="pl-k">if</span> range_header:
            <span class="pl-c"># As per RFC 2616 14.16, if an invalid Range header is specified,</span>
            <span class="pl-c"># the request will be treated as if the header didn't exist.</span>
            request_range <span class="pl-k">=</span> httputil._parse_request_range(range_header)

        <span class="pl-k">if</span> request_range:
            start, end <span class="pl-k">=</span> request_range
            size <span class="pl-k">=</span> <span class="pl-c1">self</span>.get_content_size()
            <span class="pl-k">if</span> (start <span class="pl-k">is</span> <span class="pl-k">not</span> <span class="pl-c1">None</span> <span class="pl-k">and</span> start <span class="pl-k">&gt;=</span> size) <span class="pl-k">or</span> end <span class="pl-k">==</span> <span class="pl-c1">0</span>:
                <span class="pl-c"># As per RFC 2616 14.35.1, a range is not satisfiable only: if</span>
                <span class="pl-c"># the first requested byte is equal to or greater than the</span>
                <span class="pl-c"># content, or when a suffix with length 0 is specified</span>
                <span class="pl-c1">self</span>.set_status(<span class="pl-c1">416</span>)  <span class="pl-c"># Range Not Satisfiable</span>
                <span class="pl-c1">self</span>.set_header(<span class="pl-s"><span class="pl-pds">"</span>Content-Type<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>text/plain<span class="pl-pds">"</span></span>)
                <span class="pl-c1">self</span>.set_header(<span class="pl-s"><span class="pl-pds">"</span>Content-Range<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>bytes */<span class="pl-c1">%s</span><span class="pl-pds">"</span></span> <span class="pl-k">%</span> (size, ))
                <span class="pl-k">return</span>
            <span class="pl-k">if</span> start <span class="pl-k">is</span> <span class="pl-k">not</span> <span class="pl-c1">None</span> <span class="pl-k">and</span> start <span class="pl-k">&lt;</span> <span class="pl-c1">0</span>:
                start <span class="pl-k">+=</span> size
            <span class="pl-k">if</span> end <span class="pl-k">is</span> <span class="pl-k">not</span> <span class="pl-c1">None</span> <span class="pl-k">and</span> end <span class="pl-k">&gt;</span> size:
                <span class="pl-c"># Clients sometimes blindly use a large range to limit their</span>
                <span class="pl-c"># download size; cap the endpoint at the actual file size.</span>
                end <span class="pl-k">=</span> size

            <span class="pl-c1">self</span>.set_status(<span class="pl-c1">206</span>)  <span class="pl-c"># Partial Content</span>
            <span class="pl-c1">self</span>.set_header(<span class="pl-s"><span class="pl-pds">"</span>Content-Range<span class="pl-pds">"</span></span>,
                            httputil._get_content_range(start, end, size))
        <span class="pl-k">else</span>:
            start <span class="pl-k">=</span> end <span class="pl-k">=</span> <span class="pl-c1">None</span>

        content <span class="pl-k">=</span> <span class="pl-c1">self</span>.get_content(<span class="pl-c1">self</span>.absolute_path, start, end)

        <span class="pl-k">for</span> chunk <span class="pl-k">in</span> content:
            <span class="pl-c1">self</span>.write(chunk)
            <span class="pl-k">yield</span> gen.Task(<span class="pl-c1">self</span>.flush)
        <span class="pl-c1">self</span>.finish()
</pre>
</div>
<hr />
<p>related:<br />
HTTP STATUS: 206 PARTIAL CONTENT AND RANGE REQUESTS<br />
<a href="https://benramsey.com/blog/2008/05/206-partial-content-and-range-requests/">https://benramsey.com/blog/2008/05/206-partial-content-and-range-requests/</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/03/stream-a-file-with-tornado/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ImportError: MemoryLoadLibrary failed loading netifaces.pyd</title>
		<link>https://stackoverflow.max-everyday.com/2017/03/importerror-memoryloadlibrary-failed-loading-netifaces-pyd/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/03/importerror-memoryloadlibrary-failed-loading-netifaces-pyd/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Sun, 26 Mar 2017 18:24:34 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Dropboxlike]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=394</guid>

					<description><![CDATA[netifaces 在 py2exe 打包情況下...]]></description>
										<content:encoded><![CDATA[<p>netifaces 在 py2exe 打包情況下會出錯：</p>
<p>File &#8220;zipextimporter.pyc&#8221;, line 98, in load_module<br />
ImportError: MemoryLoadLibrary failed loading netifaces.pyd</p>
<p>錯誤畫面載圖：</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-395" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2017/03/Screenshot-2017-03-27-01.08.39.png" alt="" width="1022" height="360" /></p>
<p>&nbsp;</p>
<p>發生的原因是 py2exe 無法透過 data_files 參數，或 includes 或 packages 這些參數把 netifaces.pyd 打包進去。</p>
<p>由於 py2exe for python 2.7 已經沒有在維護，不建議繼續使用，所以改用python 3 + py2exe 新版，或 pyinstaller + python 2.7.</p>
<p>全部改寫成 python 3 太累，太多 code 要重測，所以切換到 pyinstaller 比較快。</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/03/importerror-memoryloadlibrary-failed-loading-netifaces-pyd/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to close an ActionMode menu programmatically?</title>
		<link>https://stackoverflow.max-everyday.com/2017/03/how-to-close-an-actionmode-menu-programmatically/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/03/how-to-close-an-actionmode-menu-programmatically/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Thu, 23 Mar 2017 18:36:47 +0000</pubDate>
				<category><![CDATA[Android筆記]]></category>
		<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Dropboxlike]]></category>
		<category><![CDATA[UI]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=390</guid>

					<description><![CDATA[這個 ActionMode 用起來滿神奇的，他幾...]]></description>
										<content:encoded><![CDATA[<p>這個 ActionMode 用起來滿神奇的，他幾乎算是一個特定區塊的dialog，但是和 dialog 不同的是會和上一個 activity overlap 在一起，並可以存取 parent fragment 或 activity 裡的變數內容，更像是 toolbar 的概念。</p>
<p>要開始／結束 ActionMode 是用：</p>
<p>Whenever you are creating/starting ActionMode Create by</p>
<pre class="default prettyprint prettyprinted"><code><span class="pln">mMode </span><span class="pun">=</span><span class="pln"> startActionMode</span><span class="pun">(....);</span></code></pre>
<p>To Dismiss it use following Syntax</p>
<pre class="default prettyprint prettyprinted"><code><span class="kwd">if</span> <span class="pun">(</span><span class="pln">mMode </span><span class="pun">!=</span> <span class="kwd">null</span><span class="pun">)</span> 
 <span class="pun">{</span><span class="pln">
     mMode</span><span class="pun">.</span><span class="pln">finish</span><span class="pun">();</span>
 <span class="pun">}

</span></code></pre>
<p>from:<br />
<a href="http://stackoverflow.com/questions/11158957/how-to-close-an-actionmode-menu-programmatically-on-honeycomb">http://stackoverflow.com/questions/11158957/how-to-close-an-actionmode-menu-programmatically-on-honeycomb</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/03/how-to-close-an-actionmode-menu-programmatically/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Python tornado multi task</title>
		<link>https://stackoverflow.max-everyday.com/2017/03/python-tornado-multi-task/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/03/python-tornado-multi-task/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Thu, 16 Mar 2017 13:37:54 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Dropboxlike]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=370</guid>

					<description><![CDATA[透過 IOLoop 的add_callback ...]]></description>
										<content:encoded><![CDATA[<p>透過 IOLoop 的add_callback + self.finish() 可以讓 tornado 繼續在背景工作，但有一個問題是前一個工作太忙，會造成整個伺服器無法接受新的 request.</p>
<p>這要寫的有效率，似乎滿難的。</p>
<p>發現把某一個 function 加入 @gen.coroutine，主程式遇到這一個 function 自動變成 muti-thread .. @_@;</p>
<p>&nbsp;</p>
<h4>相關文章：</h4>
<p>Tornado &#8211; getting return value from IOLoop.add_callback()<br />
<a href="http://stackoverflow.com/questions/33742023/tornado-getting-return-value-from-ioloop-add-callback">http://stackoverflow.com/questions/33742023/tornado-getting-return-value-from-ioloop-add-callback</a></p>
<p>I&#8217;m not sure what the difference is between the following two functions:</p>
<pre><code> IOLoop.add_callback(callback, *args, **kwargs) 
</code></pre>
<p>AND</p>
<pre><code>IOLoop.spawn_callback(callback, *args, **kwargs)</code></pre>
<hr />
<p>You can&#8217;t get a return value, at least not in the way you&#8217;re thinking of. Your callback can&#8217;t even run until the function that called <code>spawn_callback</code> (i.e. <code>on_message</code>) returns. Just do whatever you want to do when that callback finishes in the callback itself (or pass it another callback to invoke with its result).</p>
<p><code>stack_context</code> is an error-handling mechanism for callback-oriented code. It doesn&#8217;t let you handle exceptions in the calling function, but it does let you establish an error handler in the calling function that will &#8220;follow&#8221; the code even through a chain of multiple <code>add_callback</code> calls. This turned out to be confusing as often as it helped, so now that coroutines are available I recommend using coroutines as much as possible and when you do need to fall back to raw callbacks, use <code>spawn_callback</code> instead of <code>add_callback</code> and put your try/except handler in the callback itself.</p>
<hr />
<p><a href="https://huangxiaohen2738.gitbooks.io/tornado-tcp-program/content/chapter2-2.html">https://huangxiaohen2738.gitbooks.io/tornado-tcp-program/content/chapter2-2.html</a></p>
<h2 id="22-ioloop的回调函数">2.2 ioloop的回调函数</h2>
<h5 id="1ioloopaddcallbackcallback-args-kwargs">1.IOLoop.add_callback(callback, <em>args, *</em>kwargs)</h5>
<p class="comments-section">这个函数是最简单的，在ioloop开启后执行的回调函数callback，<em>args和*</em>kwargs都是这个回调函数的参数。一般我们的server都是单进程单线程的，即使是多线程，那么这个函数也是安全的。</p>
<h5 id="8ioloopspawncallbackcallback-args-kwargs">8.IOLoop.spawn_callback(callback, <em>args, *</em>kwargs)</h5>
<p class="comments-section">这个函数也是去执行一个回调函数，但是和上面说过的其他callback不同，它和回调者的栈上下文没有关联，因此呢，他比较时候去做一些独立的功能回调。</p>
<hr />
<p>Tornado Execution Order Spawn callback<br />
<a href="http://stackoverflow.com/questions/38696131/tornado-execution-order-spawn-callback">http://stackoverflow.com/questions/38696131/tornado-execution-order-spawn-callback</a></p>
<p>No, in fact you&#8217;re guaranteed that all the functions are spawned before any of them starts running, because <code>first</code> does not <code>yield</code> between spawning <code>func</code> and spawning <code>func2</code>. You can verify this yourself by testing your code:</p>
<pre><code>from tornado import gen, ioloop

@gen.coroutine
def func():
    print('func started')
    yield gen.moment
    print('func done')


@gen.coroutine
def func2():
    print('func2 started')
    yield gen.moment
    print('func2 done')


@gen.coroutine
def first():
    for i in range(2):
        ioloop.IOLoop.current().spawn_callback(func)

    ioloop.IOLoop.current().spawn_callback(func2)
    yield gen.sleep(1)

ioloop.IOLoop.current().run_sync(first)
</code></pre>
<p>It prints:</p>
<pre><code>func started
func started
func2 started
func done
func done
func2 done
</code></pre>
<p>See, <code>func2</code> begins before the coroutines running <code>func</code> complete.</p>
<p>To accomplish what you want:</p>
<pre><code>@gen.coroutine
def first():
    yield [func() for i in range(2)]
    ioloop.IOLoop.current().spawn_callback(func2)
</code></pre>
<p>This prints:</p>
<pre><code>func started
func started
func done
func done
func2 started
func2 done
</code></pre>
<p>If you want <code>first</code> to wait for <code>func2</code> to finish before it exits, then:</p>
<pre><code>@gen.coroutine
def first():
    yield [func() for i in range(2)]
    yield func2()
</code></pre>
<p>For more info on calling coroutines from coroutines, see my <a href="https://emptysqua.re/blog/refactoring-tornado-coroutines/" rel="nofollow">Refactoring Tornado Coroutines</a>.</p>
<hr />
<p>官方文件：</p>
<p><a href="http://www.tornadoweb.org/en/stable/ioloop.html">http://www.tornadoweb.org/en/stable/ioloop.html</a></p>
<p>&nbsp;</p>
<h3>Running in the background</h3>
<p><a class="reference internal" title="tornado.ioloop.PeriodicCallback" href="http://www.tornadoweb.org/en/stable/ioloop.html#tornado.ioloop.PeriodicCallback"><code class="xref py py-obj docutils literal"><span class="pre">PeriodicCallback</span></code></a> is not normally used with coroutines. Instead, a coroutine can contain a <code class="docutils literal"><span class="pre">while</span> <span class="pre">True:</span></code> loop and use <a class="reference internal" title="tornado.gen.sleep" href="http://www.tornadoweb.org/en/stable/gen.html#tornado.gen.sleep"><code class="xref py py-obj docutils literal"><span class="pre">tornado.gen.sleep</span></code></a>:</p>
<div class="highlight-python">
<div class="highlight">
<pre><span class="nd">@gen.coroutine</span>
<span class="k">def</span> <span class="nf">minute_loop</span><span class="p">():</span>
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="k">yield</span> <span class="n">do_something</span><span class="p">()</span>
        <span class="k">yield</span> <span class="n">gen</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span>

<span class="c1"># Coroutines that loop forever are generally started with</span>
<span class="c1"># spawn_callback().</span>
<span class="n">IOLoop</span><span class="o">.</span><span class="n">current</span><span class="p">()</span><span class="o">.</span><span class="n">spawn_callback</span><span class="p">(</span><span class="n">minute_loop</span><span class="p">)</span>
</pre>
</div>
</div>
<p>Sometimes a more complicated loop may be desirable. For example, the previous loop runs every <code class="docutils literal"><span class="pre">60+N</span></code> seconds, where <code class="docutils literal"><span class="pre">N</span></code> is the running time of <code class="docutils literal"><span class="pre">do_something()</span></code>. To run exactly every 60 seconds, use the interleaving pattern from above:</p>
<div class="highlight-python">
<div class="highlight">
<pre><span class="nd">@gen.coroutine</span>
<span class="k">def</span> <span class="nf">minute_loop2</span><span class="p">():</span>
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">nxt</span> <span class="o">=</span> <span class="n">gen</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span>   <span class="c1"># Start the clock.</span>
        <span class="k">yield</span> <span class="n">do_something</span><span class="p">()</span>  <span class="c1"># Run while the clock is ticking.</span>
        <span class="k">yield</span> <span class="n">nxt</span>             <span class="c1"># Wait for the timer to run out.</span></pre>
</div>
</div>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/03/python-tornado-multi-task/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
