<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>台灣微軟學生大使</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/microsoft_student_partners_in_taiwan/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/microsoft_student_partners_in_taiwan/feed.xml</link>
    <description>Microsoft Student Partners in Taiwan</description>
    <lastBuildDate>Sun, 25 Mar 2018 02:50:11 GMT</lastBuildDate>
    <language>zh_TW</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>2018 Imagine Cup 報名教學！</title>
      <link>https://docs.microsoft.com/archive/blogs/microsoft_student_partners_in_taiwan/2018-imaging-cup-submit</link>
      <pubDate>Sat, 24 Mar 2018 14:43:39 GMT</pubDate>
      <dc:creator><![CDATA[Microsoft Student Partners in Taiwan]]></dc:creator>
      <guid
        isPermaLink="false">http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?p=7975</guid>
      <description><![CDATA[又到了一年一度的 Imagine Cup 這個由 Microsoft 針對學生所舉辦的科技大賽的報名時間，就讓 MSP 來告訴你怎麼樣完美的完成報名吧！



目錄：快速掌握重點！...]]></description>
      <content:encoded><![CDATA[又到了一年一度的 Imagine Cup 這個由 Microsoft 針對學生所舉辦的科技大賽的報名時間，就讓 MSP 來告訴你怎麼樣完美的完成報名吧！

<!--more-->
<h2><a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=8095"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/edm_01.jpg" alt="" class="aligncenter size-full wp-image-8095" width="800" height="320" /></a><a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2018/03/24/2018-imaging-cup-submit/edm_03/"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/edm_03.jpg" alt="" class="aligncenter size-full wp-image-8115" width="800" height="320" /></a><a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=8095"></a><a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2018/03/24/2018-imaging-cup-submit/edm_04/"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/edm_04.jpg" alt="" class="aligncenter size-full wp-image-8105" width="800" height="345" /></a><a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=8095"></a></h2>
<h2><strong><span style="color: #622c8a">目錄：快速掌握重點！</span></strong></h2>
<ul>
 	<li><span style="color: #622c8a"><a href="#step1">Step 1. 登入 Microsoft Imagine</a></span></li>
 	<li><span style="color: #622c8a"><a href="#step2">Step 2. 確認 Microsoft Imagine 帳戶資訊</a></span></li>
 	<li><span style="color: #622c8a"><a href="#step3">Step 3. 報名 2018 Imagine Cup！</a></span></li>
 	<li><span style="color: #622c8a"><a href="#step4">Step 4. 開始建立團隊！</a></span></li>
 	<li><span style="color: #622c8a"><a href="#step5">Final Step！上傳團隊專案</a></span></li>
</ul>
&nbsp;

<hr />

<h2><span style="color: #5d2e8f">Step 1. 登入 Microsoft Imagine</span></h2>
首先請在看文章的你，點選<em><strong><a href="https://imagine.microsoft.com/zh-tw"> 這裡</a></strong></em> 進到 Microsoft Imagine 的首頁。
接著請點選<span style="color: #ff0000">右上角的登入</span>！

[caption id="attachment_7995" align="aligncenter" width="1024"]<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7995"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/00_ichome-1024x660.png" alt="" class="wp-image-7995 size-large" width="1024" height="660" /></a> Microsoft Imagine 首頁[/caption]

之後請登入你的<strong><span style="color: #ff0000">微軟帳號（非學生信箱）</span></strong>，若沒有微軟帳號請點選「建立一個吧！」的按鈕。

&nbsp;

<hr />

<h2><span style="color: #5d2e8f">Step 2. 確認 Microsoft Imagine 帳戶資訊
</span></h2>
如果是先前已經使用過 Micrsoft Imagine 的帳號，會直接跳到 Micrsoft Imagine 帳戶管理頁面，如圖：

請點選左方的編輯進行資料確認。

[caption id="attachment_8005" align="aligncenter" width="1024"]<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=8005"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/02c_done-1024x659.png" alt="" class="wp-image-8005 size-large" width="1024" height="659" /></a> 非第一次使用 Microsoft Imagine[/caption]

如果是<span style="color: #ff0000">新建立</span>的帳號則會請你直接輸入資料：

[caption id="attachment_8015" align="aligncenter" width="1024"]<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=8015"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/02a_first_personalinformation-1024x660.png" alt="" class="wp-image-8015 size-large" width="1024" height="660" /></a> 第一次登入 Microsoft Imagine[/caption]

<strong><span style="color: #ff0000">※要注意國家與 Imagine 競賽地區都要選擇台灣喔！</span></strong>

<strong><span style="color: #ff0000">※完成之後請回到帳戶頁面進行 Imagine Cup 的報名與建立隊伍！</span></strong>

<a></a>

<hr />

<h2><span style="color: #5d2e8f">Step 3. 報名 2018 Imagine Cup！</span></h2>
回到帳戶頁面之後點選 2018 Imagine Cup 下方的立即報名按鈕。

[caption id="attachment_8025" align="aligncenter" width="1024"]<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=8025"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/02c_done-copy-1024x659.png" alt="" class="wp-image-8025 size-large" width="1024" height="659" /></a> 進行 Imagine Cup 的報名[/caption]

稍待片刻之後會跳轉到報名完成的頁面，提示你已完成報名！

[caption id="attachment_8035" align="aligncenter" width="1024"]<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=8035"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/03_great-1024x660.png" alt="" class="wp-image-8035 size-large" width="1024" height="660" /></a> 報名完成的提示頁面[/caption]

看到這個頁面之後就可以進入下一步囉！

<a></a>

<hr />

<h2><span style="color: #5d2e8f">Step 4. 來建立團隊吧！</span></h2>
從上面的頁面中點選左上方的帳戶按鈕，回到帳戶管理頁面。
接著請往下滑動頁面，在<span style="color: #ff0000">建立團隊</span>的區塊停下，輸入你的隊名並點選提交！

[caption id="attachment_8045" align="aligncenter" width="1024"]<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=8045"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/05_group-1024x660.png" alt="" class="wp-image-8045 size-large" width="1024" height="660" /></a> 新增團隊[/caption]

點選提交之後會出現下列畫面，部分資訊會由系統直接帶入，請將其他必填選項完成。

[caption id="attachment_8055" align="aligncenter" width="1024"]<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=8055"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/06_creat-1024x659.png" alt="" class="wp-image-8055 size-large" width="1024" height="659" /></a> 填寫團隊資訊[/caption]

當你建立完團隊，回到 Microsoft Imagine 帳戶管理頁面就可以看到自己剛剛所建立的團隊囉！

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=8075"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/07_groupdone-1024x659.png" alt="" class="aligncenter size-large wp-image-8075" width="1024" height="659" /></a>

這時候只需要點選管理，便可以進行團隊資訊的修改與新增隊員（<strong><span style="color: #ff0000">至多 3 人</span></strong>）。
<blockquote>
<p style="text-align: left"><span style="color: #ff0000"><strong>至多 3 人</strong></span></p>
<p style="text-align: left"><span style="color: #ff0000"><strong>至多 3 人</strong></span></p>
</blockquote>
<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=8065"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/08_addmember-1024x660.png" alt="" class="aligncenter size-large wp-image-8065" width="1024" height="660" /></a>

恭喜你完成 2018 Imagine Cup 的報名！

<a></a>

<hr />

<h2><span style="color: #5d2e8f">Final Step！上傳團隊的專案</span></h2>
前往參加比賽的最後一個步驟，提交要參賽的專案！

在進行專案的提交前請各位先留意需要提供的內容以及評選標準：
<ol>
 	<li>專案標題</li>
 	<li>專案敘述（最多 255 個字元）</li>
 	<li>專案提案（內容請包涵團隊與專案的介紹）
<ul>
 	<li>Word 或是 PDF 檔案不能超過 10 頁</li>
 	<li>PPT 檔案不能超過 20 張</li>
 	<li>檔案限制為 100 MB</li>
</ul>
</li>
 	<li>專案檔案
<ul>
 	<li>只接受 ZIP</li>
 	<li>大小限制為 1GB，超過請按照說明操作</li>
</ul>
</li>
 	<li>專案檔案介紹
<ul>
 	<li>只接受 DOC、DOCX、PDF 與 ZIP</li>
 	<li>大小限制為 10 MB</li>
</ul>
</li>
 	<li>其他選填項目</li>
 	<li>評選標準：<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2018/03/24/2018-imaging-cup-submit/edm_06/"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/edm_06.jpg" alt="" class="aligncenter size-full wp-image-8135" width="800" height="206" /></a></li>
</ol>
回到 Microsoft Imagine 的帳戶管理頁面，在 2018 Imagine Cup 的地方點選「<span style="color: #ff0000">提交 &gt;</span>」的按鈕，便會跳轉到上傳專案的頁面。

&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=8085"><img src="https://msdnshared.blob.core.windows.net/media/2018/03/04_ggreee-1024x659.png" alt="" class="aligncenter size-large wp-image-8085" width="1024" height="659" /></a>

&nbsp;

上傳完所有指定項目之後，按下提交就可以囉！]]></content:encoded>
    </item>
    <item>
      <title>【.NET Core】.NET Core 從入門到反向</title>
      <link>https://docs.microsoft.com/archive/blogs/microsoft_student_partners_in_taiwan/%E3%80%90-net-core%E3%80%91-net-core-%E5%BE%9E%E5%85%A5%E9%96%80%E5%88%B0%E5%8F%8D%E5%90%91</link>
      <pubDate>Thu, 26 Oct 2017 15:04:56 GMT</pubDate>
      <dc:creator><![CDATA[Microsoft Student Partners in Taiwan]]></dc:creator>
      <guid
        isPermaLink="false">http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?p=7817</guid>
      <description><![CDATA[.NET 系列

 	.NET 以下各式各樣的變種，這裡簡單介紹一下

.NET Core

 	這是這次主要要介紹的重點。
 	主要特點是能跨平台，但是不支援一些 GUI...]]></description>
      <content:encoded><![CDATA[<div id="doc" class="markdown-body container-fluid">
<h2 id="net-系列">.NET 系列</h2>
<ul>
 	<li>.NET 以下各式各樣的變種，這裡簡單介紹一下</li>
</ul>
<h3 id="net-core">.NET Core</h3>
<ul>
 	<li>這是這次主要要介紹的重點。</li>
 	<li>主要特點是能跨平台，但是不支援一些 GUI 的介面，適合拿來做 CLI 介面或是網路等無介面的應用。</li>
</ul>
<h3 id="net-framework">.NET Framework</h3>
<ul>
 	<li>主要是用來開發 Windows PC 上面的應用程式，但是跨平台能力較差，但是可以開發出各式各樣 PC 上面的應用程式。</li>
 	<li>支援使用多種語言，如 C#、C++、VB…</li>
</ul>
<h3 id="xamarin">Xamarin</h3>
<ul>
 	<li>主要的功能是開發跨平台的行動裝置原生應用程式，可以寫一份 code 就能在多個行動裝置上執行</li>
</ul>
<h3 id="mono">Mono</h3>
<ul>
 	<li>一個在 .NET Core 出來之前跨平台的 C# 執行環境&amp;編譯器</li>
 	<li>其專案也支援用 Gtk 開發桌面環境的程式</li>
 	<li>後來成為了 Xamarin 的前身</li>
</ul>
<h3 id="net-standard">.NET Standard</h3>
<ul>
 	<li>如前面所述，.Net 有許許多多的分支，但是這些分支之間的 API 卻不一致，因此這個專案是為了讓開發人員可以使用一組共同的可攜式程式庫，使得一些和作業系統不相關的程式碼可以被簡單的共用。</li>
</ul>
<h2 id="官方網頁">官方網頁</h2>
<ul>
 	<li><a target="_blank" href="https://www.microsoft.com/net/" rel="noopener noreferrer">https://www.microsoft.com/net/</a></li>
 	<li><a target="_blank" href="https://docs.microsoft.com/zh-tw/dotnet/core/" rel="noopener noreferrer">https://docs.microsoft.com/zh-tw/dotnet/core/</a></li>
</ul>
<h2 id="入門">入門</h2>
<h3 id="安裝開發環境">安裝開發環境</h3>
<ul>
 	<li>照著這個頁面上的步驟一步一步安裝 <a target="_blank" href="https://www.microsoft.com/net/download/core" rel="noopener noreferrer">https://www.microsoft.com/net/download/core</a></li>
</ul>
<h3 id="撰寫第一個-net-core-程式">撰寫第一個 .NET Core 程式</h3>
<ul>
 	<li>在電腦上打開終端機，輸入以下的指令</li>
</ul>
<pre><code class="sh hljs">dotnet <span class="hljs-keyword">new</span> <span class="hljs-built_in">console</span> -o hwapp
cd hwapp
</code></pre>
<ul>
 	<li>在那個資料夾中加入一個名為 <code>Program.cs</code> 檔案，並包含以下的內容</li>
</ul>
<pre><code class="C hljs"><span class="hljs-keyword">using</span> System;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">hwapp</span>
{
    <span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>[] args</span>)
        </span>{
            Console.WriteLine(<span class="hljs-string">"Hello World!"</span>);
        }
    }
}
</code></pre>
<ul>
 	<li>這個程式的功能是在螢幕上印出 <code>Hello World!</code> 字樣</li>
 	<li>使用以下的指令執行他，就能看到螢幕上印出 <code>Hello World!</code></li>
</ul>
<pre><code class="sh hljs"><span class="hljs-attribute">dotnet</span> run
</code></pre>
<h3 id="簡單小練習">簡單小練習</h3>
<ul>
 	<li>利用 .NET Core 輸出九九乘法表</li>
</ul>
<pre><code class="C hljs"><span class="hljs-keyword">using</span> System;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">hwapp</span>
{
    <span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
    {
        <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>[] args</span>)
        </span>{
            <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">1</span>;i&lt;=<span class="hljs-number">9</span>;i++)
                <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> j=<span class="hljs-number">1</span>;j&lt;=<span class="hljs-number">9</span>;j++)
                    Console.WriteLine(i+<span class="hljs-string">"*"</span>+j+<span class="hljs-string">"="</span>+i*j);
        }
    }
}
</code></pre>
<h3 id="跨平台執行">跨平台執行</h3>
<ul>
 	<li>可以使用以下的指令把他 build 起來，之後到其他平台上就可以輕鬆地使用</li>
</ul>
<pre><code>dotnet build
</code></pre>
<ul>
 	<li>如果是想要輸出成 Release 版本的話，就使用以下的指令</li>
</ul>
<pre><code>dotnet build -c Release
</code></pre>
<ul>
 	<li>然後你會看到他輸出了一行類似於 <code>XXX -&gt; &lt;Path&gt;/XXX.dll</code> 的東西，而那個路徑就是你的目標檔案</li>
 	<li>把那個路徑下的 <code>XXX.dll</code> 和 <code>XXX.deps.json</code> 拿到其他裝有 .NET Core Runtime 的環境，並且執行</li>
</ul>
<pre><code>dotnet XXX.dll
</code></pre>
<ul>
 	<li>就會發現他成功地在其他環境中跑起來了</li>
</ul>
<h2 id="反向">反向</h2>
<h3 id="工具">工具</h3>
<ul>
 	<li>對於 .NET 系列的程式做逆向工程我會推薦使用一個名為 <code>dnSpy</code> 的開源軟體來達成這件事情
<ul>
 	<li>官方 Github <a target="_blank" href="https://github.com/0xd4d/dnSpy" rel="noopener noreferrer">https://github.com/0xd4d/dnSpy</a></li>
 	<li>Release <a target="_blank" href="https://github.com/0xd4d/dnSpy/releases" rel="noopener noreferrer">https://github.com/0xd4d/dnSpy/releases</a></li>
</ul>
</li>
 	<li>下面是他 Github 上的示範動畫
<img src="https://raw.githubusercontent.com/0xd4d/dnSpy/master/images/debug-animated.gif" alt="" />

<img src="https://raw.githubusercontent.com/0xd4d/dnSpy/master/images/edit-code-animated.gif" alt="" /></li>
 	<li>安裝方法很簡單，從 Release 那裡載下來後解壓縮就能直接使用了</li>
</ul>
<h3 id="入門1">入門</h3>
<ul>
 	<li>首先我們把我們之前寫好的 .NET Core 應用程式的 dll 拿出來，拖曳到dnspy的視窗裡面，會發現到畫面左邊出現的樹狀目錄可以點，點一點之後就能找到我們寫的應用程式的程式內容
<img alt="" /></li>
 	<li>這時你會發現我們的程式居然被 dnSpy 反編譯成原本的樣子了</li>
 	<li>接下來我們試著去修改這隻程式</li>
 	<li>對著要修改的程式按右鍵後選擇 <code>Edit Method (C#)...</code>
<img alt="" /></li>
 	<li>接著你就會看到一個視窗，你可以在裡面把原本的函數改成你想要的樣子
<img alt="" />
<img alt="" /></li>
 	<li>接著按下 <code>compile</code> 按鈕</li>
 	<li>然後從上面選單中的 <code>File</code> 裡選擇 <code>Save Module...</code> 然後按下 <code>OK</code> 後就修改完成了</li>
 	<li>使用 .NET Core 執行看看，看看他是否真的改變了
<img alt="" /></li>
</ul>
<h3 id="ctf中的實例">CTF中的實例</h3>
<ul>
 	<li>這次的目標是 D-CTF Quals 2017 裡面的 Don't net, kids! 這一題</li>
 	<li>把題目給的 zip 載下來後會發現其實是一個 .NET Core 的 網頁程式</li>
 	<li>把裡面的 <code>DCTFNetCoreWebApp.dll</code> 丟到 dnSpy 裡面分析</li>
 	<li>觀察到 <code>DCTFNetCoreWebApp.Controllers</code> 這個 namespace 裡面的東西，發現這個是負責做路徑解析的功能，然後再看到裡面的 <code>Post</code> 函式
<img alt="" /></li>
 	<li>發現到以下的內容</li>
</ul>
<pre><code class="C hljs"><span class="hljs-built_in">string</span> expr_69 = JsonConvert.SerializeObject(<span class="hljs-keyword">this</span>._executor.Execute(request.Command));
</code></pre>
<ul>
 	<li>連點兩下 <code>Execute</code> 跳轉進去那個函式的內容
<img alt="" /></li>
 	<li>然後觀察到這行</li>
</ul>
<pre><code class="C hljs"><span class="hljs-keyword">bool</span> flag = command.GetType().get_Name().Equals(<span class="hljs-keyword">typeof</span>(AdminCommand).get_Name());
</code></pre>
<ul>
 	<li>仔細想想那行所代表的意思，發現其意思是: 如果 Command 的型態是 AdminCommand 的話，則代表這個 Command 可以執行被包含在 _amdinActions 內的 Command</li>
 	<li>這時我們的目標變成，如何讓輸入的 Command 會被轉型成 AdminCommand</li>
 	<li>這時我們回想起了 <code>Post</code> 函式裡的某個行程式碼</li>
</ul>
<pre><code class="C hljs">Request request = JsonConvert.DeserializeObject&lt;Request&gt;(<span class="hljs-keyword">value</span>, <span class="hljs-keyword">new</span> JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Auto
});
</code></pre>
<ul>
 	<li>因為 <code>TypeNameHandling</code> 被設定成了 <code>TypeNameHandling.Auto</code>，因此我們可以構造一個 json，而其內容中含有"$type"，使得他會被轉型成想要的型態</li>
 	<li>最後我們使用以下的 json 來得到 Flag</li>
</ul>
<pre><code>{
    "Command": {
        "$type": "DCTFNetCoreWebApp.Models.AdminCommand, DCTFNetCoreWebApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
        "UserId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
        "Action": "Readflag"
    }
}
</code></pre>
<h3 id="接續">接續</h3>
<ul>
 	<li>有時程式碼會被經過大量的混淆，那麼這時 dnSpy 出來的東西會變得很難看，這時就會需要花大量的耐心去仔細的觀察資料的流動和程式的邏輯，把裡面程式碼裡面重要和不要的部分分清楚，了解程式的核心，最後再找到目標，並且做到想要做的事情</li>
</ul>
<h2 id="額外">額外</h2>
<ul>
 	<li>以下是幾個我覺得和 .NET 有關，而且蠻有趣的專案</li>
</ul>
<h3 id="peachpie">peachpie</h3>
<ul>
 	<li><a target="_blank" href="https://github.com/peachpiecompiler/peachpie" rel="noopener noreferrer">https://github.com/peachpiecompiler/peachpie</a></li>
 	<li>一個用 .NET 寫的 PHP 7 編譯器和執行環境</li>
</ul>
<h3 id="sshnet"><a target="_blank" href="http://SSH.NET" rel="noopener noreferrer">SSH.NET</a></h3>
<ul>
 	<li><a target="_blank" href="https://github.com/sshnet/SSH.NET" rel="noopener noreferrer">https://github.com/sshnet/SSH.NET</a></li>
 	<li>.NET 用的 SSH library</li>
</ul>
</div>
&nbsp;
<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/10/03/wsltune/yi-chou/"><img src="https://msdnshared.blob.core.windows.net/media/2017/09/Yi-Chou.png" alt="" class="size-full wp-image-7696 alignleft" width="200" height="200" /></a>

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

<span style="color: #be017a">周逸 Yi Chou</span>

Microsoft Student Partners in Taiwan
專長：C/C++、C#'PHP
GitHub:<a href="https://github.com/qazwsxedcrfvtg14">qazwsxedcrfvtg14</a>]]></content:encoded>
    </item>
    <item>
      <title>【LUIS】在 10 分鐘內創造您的第一個 LUIS APP</title>
      <link>https://docs.microsoft.com/archive/blogs/microsoft_student_partners_in_taiwan/%E3%80%90luis%E3%80%91%E5%9C%A8-10-%E5%88%86%E9%90%98%E5%85%A7%E5%89%B5%E9%80%A0%E6%82%A8%E7%9A%84%E7%AC%AC%E4%B8%80%E5%80%8B-luis-app</link>
      <pubDate>Thu, 26 Oct 2017 15:03:35 GMT</pubDate>
      <dc:creator><![CDATA[Microsoft Student Partners in Taiwan]]></dc:creator>
      <guid
        isPermaLink="false">http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?p=7826</guid>
      <description><![CDATA[此 Quickstart 可以在短短的幾分鐘內幫助您創建您的第一個語言理解智能服務（LUIS）應用程序。完成後，您將有一個 LUIS...]]></description>
      <content:encoded><![CDATA[<div id="doc" class="markdown-body container-fluid">

此 Quickstart 可以在短短的幾分鐘內幫助您創建您的第一個語言理解智能服務（LUIS）應用程序。完成後，您將有一個 LUIS 終端在雲端運行。在範例中，您將創建一個旅遊應用程序，可幫助您預訂航班，並檢查目的地的天氣。“how-to” 是指這個應用程序，並建立在它上面。

<!--more-->
<h2 id="在開始之前">在開始之前</h2>
為了使用 Microsoft Cognitive Service APIs，您需先在 Azure 儀表板創立一個 <a target="_blank" href="https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account" rel="noopener noreferrer">Cognitive Services API account</a>.

如果您沒有 Azure 訂閱，先註冊 <a target="_blank" href="https://azure.microsoft.com/free/?WT.mc_id=A261C142F" rel="noopener noreferrer">free account</a>
<h2 id="製作一個新的-app">製作一個新的 APP</h2>
您可以在 <strong>My Apps</strong> 頁面上創建和管理您的應用程序。您可以透過點擊 <a target="_blank" href="https://www.luis.ai" rel="noopener noreferrer">LUIS web page</a> 頂端導航欄中的 <strong>My Apps</strong> 來進入此頁面。
<ol>
 	<li>在 <strong>My Apps</strong> 頁面，點擊 <strong>New App</strong>.</li>
 	<li>在對話框中，將您的應用程序命名為 “TravelAgent”。<img src="https://docs.microsoft.com/en-us/azure/cognitive-services/luis/images/newapp-form.jpg" alt="" /></li>
 	<li>選擇您應用程式的語言 (在 TravelAgent 中，我們選擇英文)，並點選 <strong>Create</strong>.</li>
</ol>
<code>&gt;[!注意]
&gt;語言一旦選定就無法更動.</code>

LUIS 創建了 TravelAgent 應用程序，並打開其主頁面，如下圖所示。使用左側面板中的導航鏈接移動您的應用頁面，以定義數據並處理您的 app。

<img src="https://docs.microsoft.com/en-us/azure/cognitive-services/luis/images/appcreated_opened.jpg" alt="" />
<h2 id="添加意圖-intents">添加意圖 (intents)</h2>
您的第一個任務是添加意圖 (intents)。意圖 (intents) 是用戶的話語傳達的意圖或請求的動作。它們是您的應用程序的主要模塊。您現在需要定義應用程序所需要偵測到的意圖（例如，預訂航班）。單擊側面菜單中的 <strong>Intents</strong> 來進入 intents 頁面，通過單擊 <strong>Add Intent</strong> 按鈕來創建您的意圖 (intents)。

更詳細添加 intents 的教學，請閱讀 <a target="_blank" href="https://docs.microsoft.com/en-us/azure/cognitive-services/luis/add-intents" rel="noopener noreferrer">Add intents.</a>
<h2 id="添加語句-utterances">添加語句 (utterances)</h2>
現在您已經定義了意圖，您可以開始建立範例，以便讓機器學習模型學習到不同的模式（例如，“預訂6月8日起飛的西雅圖航班”）。選擇您剛剛添加的 intent 並將您的 utterances 保存在此 intent 內。
<h2 id="添加實體-entities">添加實體 (entities)</h2>
現在你有 intents，你可以繼續添加 entities。entities 描述與 intents 相關的信息，有時對於您的應用程序執行其任務至關重要。這個應用程序的一個例子是預訂航班的航空公司。在您的 TravelAgent 應用程序中添加一個名為 “Airline” 的 entities。

更多 entities 的詳細資訊，請詳閱 <a href="https://docs.microsoft.com/en-us/azure/cognitive-services/luis/add-entities">Add entities.</a>
<h2 id="在語句-utterances-中標註實體-entities">在語句 (utterances) 中標註實體 (entities)</h2>
接下來，您需要在例子中標註 entities 來訓練 LUIS 定義此 entities。請在您添加的話語中，特別標出 entities。
<h2 id="添加預建構實體-prebuilt-entities">添加預建構實體 (prebuilt entities)</h2>
添加一個預先存在的實體可能會很有用，我們稱之為 <em>prebuilt</em> entities。這些類型的 entities 可以直接使用，不需要標註。轉到 <strong>Entities</strong> 頁面添加與您的應用程序相關的 prebuilt entities。 將 <code>ordinal</code> 和 <code>datetime</code> 這兩個 prebuilt entities 添加到您的應用程序。
<h2 id="訓練您的-app">訓練您的 APP</h2>
在左側面板中選擇 <strong>Train &amp; Test</strong>，然後單擊 <strong>Train Application</strong> 並根據前面步驟中定義的 intents，utterances 和 entities 來訓練您的應用程序。
<h2 id="測試您的-app">測試您的 app</h2>
您可以通過鍵入測試 utterance 並按 Enter 鍵來測試。結果顯示與每個 intent 相關的得分。檢查最高評分的 intent 是否符合每個測試 utterance 的 intent。
<h2 id="發布您的-app">發布您的 app</h2>
從左側菜單中選擇 <strong>Publish App</strong>，然後單擊 <strong>Publish</strong>。
<h2 id="使用您的-app">使用您的 app</h2>
從 <strong>Publish App</strong> 頁面複製端點URL，並將其黏貼到瀏覽器中。在URL結尾附加一個查詢，如"預訂波士頓航班"，並提交請求。包含結果的 JSON 應該顯示在瀏覽器中。
<h2 id="下一步">下一步</h2>
<ul>
 	<li>嘗試通過繼續添加和標註 utterances 來提高 app 的性能。</li>
 	<li>試著添加 <a target="_blank" href="https://docs.microsoft.com/en-us/azure/cognitive-services/luis/add-features" rel="noopener noreferrer">Features</a> 來豐富您的模型並增進您 LUIS 的表現。Features 幫助您的應用識別可互換的詞/短語，以及在您的領域中較常用的用語。</li>
</ul>
</div>
&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7736"><img src="https://msdnshared.blob.core.windows.net/media/2017/10/Hsiang-Wang.png" alt="" class="size-full wp-image-7736 alignleft" width="200" height="200" /></a>

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

<span style="color: #b9027d">王項 Hsiang Wang</span>

專長：<span>C、C++</span>

GitHub：<a href="https://github.com/Victerose">Victerose</a>
翻譯至：<a href="https://docs.microsoft.com/en-us/azure/cognitive-services/luis/luis-get-started-create-app">Create your first LUIS app in ten minutes</a>]]></content:encoded>
    </item>
    <item>
      <title>【事前準備】安裝 MongoDB！</title>
      <link>https://docs.microsoft.com/archive/blogs/microsoft_student_partners_in_taiwan/preparemongo</link>
      <pubDate>Sun, 15 Oct 2017 16:38:21 GMT</pubDate>
      <dc:creator><![CDATA[Microsoft Student Partners in Taiwan]]></dc:creator>
      <guid
        isPermaLink="false">http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?p=7756</guid>
      <description><![CDATA[本教學為 2017/10/18（三）在台灣大學所舉辦的【動動手指】機器學習與智能機器人事前準備步驟。



Download URL:...]]></description>
      <content:encoded><![CDATA[本教學為 2017/10/18（三）在台灣大學所舉辦的<a href="https://www.accupass.com/go/mspmlbot">【動動手指】機器學習與智能機器人</a>事前準備步驟。

<!--more-->

Download URL: <a target="_blank" href="https://www.mongodb.com/download-center#community" rel="noopener noreferrer">https://www.mongodb.com/download-center#community</a>
<img src="https://i.imgur.com/IBum4lX.png" alt="" />
下載完後可以在目錄：C:\Program Files\MongoDB\Server\3.4\bin 找到 
<img src="https://i.imgur.com/zEFyAkv.png" alt="" />
&nbsp;
<h2 id="利用gui-應用程式-操控mongo-database">利用 GUI 應用程式 操控 MongoDB</h2>
下載 Robomongo（MongoDB management）
<a target="_blank" href="https://robomongo.org/download" rel="noopener noreferrer">https://robomongo.org/download</a>
<img src="https://i.imgur.com/1hiUGl7.png" alt="" />
&nbsp;
<h3 id="讓robomongo和你的mongo-database-做連接">讓 Robomongo 和你的 MongoDB 做連接</h3>
1.首先先利用建立一個 Database
在 C 槽建立 data 資料夾裡面再放 db 資料夾(格式需求)
<img src="https://i.imgur.com/cOCU3Fv.png" alt="" />
打開cmd(命令提示字元)
<code>
cd “C:\Program Files\MongoDB\Server\3.4\bin”（到達Mongod.exe 的所在目錄）
mongod (執行mongodb server,等待連結)</code>
&nbsp;
2.打開 Ronomongo 做連接(client 端)
<img src="https://i.imgur.com/y7jLtYa.png" alt="" />
&nbsp;
3.按下 connect 後會看到
<img src="https://i.imgur.com/sCTQ8FJ.png" alt="" />
&nbsp;
4.輸入你想要的名字（Address上的 localhost port 27017 正是你 mongoDB server 端現在在執行的port），按下 save 即完成連接你會看到
<img src="https://i.imgur.com/OCS7RfK.png" alt="" />
&nbsp;
5.完成 MongoDB 本地端的建立!!
&nbsp;
<h4 id="mongo-db的優點">MongoDB 的優點</h4>
<ul>
 	<li>No-SQL 資料庫：存入的資料類型沒有限制要每個都一樣(具彈性)</li>
 	<li>Document 資料庫：以一個個 JSON 格式的文字存入資料庫中,各程式建物件導向易於傳遞這些 JSON 資料(普遍性)
<img src="https://i.imgur.com/8t4bG98.png" alt="" /></li>
&nbsp;
 	<li>MongoDB 核心是 C++：運算操作裡面的資料迅速(快速性)
<img src="https://i.imgur.com/ftmNYmP.png" alt="" /></li>
&nbsp;
</ul>
&nbsp;
<h4 id="用robomongo的原因是因為相較於指令cmd介面能更理解資料庫中資料擺放的位置視覺化">用 Robomongo 的原因是因為相較於指令（cmd介面）能更理解資料庫中資料擺放的位置（視覺化）</h4>
&nbsp;
&nbsp;
&nbsp;
&nbsp;
<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7785"><img src="https://msdnshared.blob.core.windows.net/media/2017/10/Ming-Hsuan-Hsieh.png" alt="" width="200" height="200" class="size-full wp-image-7785 alignleft" />

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

<span style="color: #b9047d">謝明軒 Ming-Hsuan Hsieh</span>

專長：Java、C、Javascript

GitHub ：<a href="https://github.com/swes1117">swes1117</a>]]></content:encoded>
    </item>
    <item>
      <title>【WSL】Windows Subsystem for Linux 安裝及基本配置！</title>
      <link>https://docs.microsoft.com/archive/blogs/microsoft_student_partners_in_taiwan/wsltune</link>
      <pubDate>Tue, 03 Oct 2017 00:26:13 GMT</pubDate>
      <dc:creator><![CDATA[Microsoft Student Partners in Taiwan]]></dc:creator>
      <guid
        isPermaLink="false">http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?p=7545</guid>
      <description><![CDATA[WSL 是什麼，能吃嗎
Windows Subsystem for Linux（簡稱WSL）是一個為在 Windows 10...]]></description>
      <content:encoded><![CDATA[<h1>WSL 是什麼，能吃嗎</h1>
Windows Subsystem for Linux（簡稱WSL）是一個為在 Windows 10 上能夠原生運行Linux二進制可執行文件（ELF格式）的兼容層。

它是由微軟與 Canonical 公司合作開發，目標是使純正的 Ubuntu 16.04 "Xenial Xerus" 映像能下載和解壓縮到用戶的本地電腦，並且映像內的工具和實用工具能在此子系統上原生運行。

WSL 提供了一個微軟開發的 Linux 兼容內核接口（不包含 Linux 代碼），使得 Linux 用戶模式的二進制文件能在其上執行。

運行原理很像是 Linux 上 Wine 的 Windows 版。

<!--more-->
<h2>官方 Github Repo</h2>
<ul>
 	<li><a href="https://github.com/Microsoft/BashOnWindows/">Bash On Windows</a></li>
 	<li>如果遇到 bug 或是想要有新的 feature，請上去發 Issues。</li>
</ul>
<h2>Release Note</h2>
<ul>
 	<li><a href="https://msdn.microsoft.com/en-us/commandline/wsl/release_notes?f=255&amp;MSPPError=-2147217396">Bash On Windows - Release Notes</a></li>
 	<li>可以在這裡看到最新的 Windows Insider Preview 中 WSL 被改進了什麼東西，和出現了什麼bug。</li>
</ul>
<h2>安裝方法</h2>
<ul>
 	<li>以下僅考慮正式 Release 的 Windows 版本（Insider 請去看 Release Note）</li>
 	<li>如果你的電腦不是 Intel 的 64 位元架構的話（例如x86、arm架構）
<ul>
 	<li>請買新電腦</li>
</ul>
</li>
 	<li>如果你的 Windows OS 組建低於 14393
<ul>
 	<li><a href="https://support.microsoft.com/en-us/help/3159635/windows-10-update-assistant">請升級您的 Windows</a></li>
</ul>
</li>
 	<li>如果你的 Windows OS 組建是 14393
<ul>
 	<li>我還是建議<a href="https://support.microsoft.com/en-us/help/3159635/windows-10-update-assistant">升級</a>您的 Windows，因為這個版本的功能不是很完整，使用時會遇到很多坑</li>
 	<li>如果真的要安裝可以，請參考 15063 的 WSL 安裝方法</li>
</ul>
</li>
 	<li>如果你的 Windows OS 組建是 15063
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>開啟 WSL 有兩種方法，一個是用圖形介面，一個是下指令，可以自由選擇。</li>
 	<li>方法一
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>請打開 Windows【設定】（快捷鍵 Win+I）<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7555"><img src="https://msdnshared.blob.core.windows.net/media/2017/09/163.png" alt="" class="aligncenter size-full wp-image-7555" width="1241" height="807" /></a></li>
</ul>
</li>
</ul>
&nbsp;
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>打開【更新與安全性】，進去【開發人員專用】分頁，勾選【開發人員模式】<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7565"><img src="https://msdnshared.blob.core.windows.net/media/2017/09/223.png" alt="" class="aligncenter size-full wp-image-7565" width="1241" height="807" /></a></li>
</ul>
</li>
</ul>
&nbsp;
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>這時系統可能會叫你重開機，如果有的話，請重開</li>
 	<li>進到【設定】裡的【APP】，然後點選畫面右方（有時會在畫面最下方）的【程式與功能】<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7575"><img src="https://msdnshared.blob.core.windows.net/media/2017/09/312.png" alt="" class="aligncenter size-full wp-image-7575" width="1241" height="807" /></a></li>
</ul>
</li>
</ul>
&nbsp;
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>然後點選【開啟或關閉 Windows 功能】，在裡面找到【適用於 Linux 的 Windows 子系統（搶鮮版（Beta））】勾選後按【確定】<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7585"><img src="https://msdnshared.blob.core.windows.net/media/2017/09/412.png" alt="" class="aligncenter size-full wp-image-7585" width="889" height="851" /></a></li>
</ul>
</li>
</ul>
&nbsp;</li>
 	<li>方法二
<ul>
 	<li>打開管理員權限的 Powershell，輸入以下指令
<code>Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux</code></li>
</ul>
</li>
 	<li>跑了一陣子之後 Windows 會叫你重新開機，然後請重新開機。</li>
 	<li>重開機完後 WSL 已經安裝完成。</li>
 	<li>接下來要安裝 Ubuntu 的映像</li>
 	<li>叫出【執行】（快捷鍵 Win+R），輸入【bash】後按確定<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7586"><img src="https://msdnshared.blob.core.windows.net/media/2017/09/55.png" alt="" class="aligncenter size-full wp-image-7586" width="399" height="206" /></a></li>
</ul>
</li>
</ul>
&nbsp;
<ul>
 	<li>接下來他會問一推問題，請依照需求回答，例如使用者帳號密碼之類的。</li>
 	<li>以後要打開他可以直接叫出【執行】（快捷鍵 Win+R），輸入【bash】後按確定後打開。
<ul>
 	<li>其執行檔位置在【C:\Windows\System32\bash.exe】</li>
</ul>
</li>
 	<li>WSL 的管理方法
<code>lxrun
在 LX 子系統上執行系統管理作業</code>
<code>使用方式:
/install - 安裝子系統
選擇性引數:
/y - 不提示使用者接受或建立子系統使用者
/uninstall - 解除安裝子系統
選擇性引數:
/full - 執行完整解除安裝
/y - 不提示使用者確認
/setdefaultuser - 設定 bash 將以其身分啟動的子系統使用者。若該使用者不存在，將會建立該使用者。
選擇性引數:
username - 提供使用者名稱
/y - 若提供使用者名稱，不提示建立密碼
/update - 更新子系統的套件索引</code></li>
</ul>
</li>
</ul>
&nbsp;
<ul>
 	<li>如果你的 Windows OS 組建是 16215 以上
<ul>
 	<li>首先先參考 15063 的 WSL 安裝方式</li>
 	<li>然後到 Windows Store 上下載喜歡的 Linux 映像</li>
 	<li><a href="https://www.microsoft.com/en-us/store/p/ubuntu/9nblggh4msv6">Ubuntu</a></li>
 	<li><a href="https://www.microsoft.com/en-us/store/p/opensuse-leap-42/9njvjts82tjx">openSUSE Leap 42</a></li>
 	<li><a href="https://www.microsoft.com/en-us/store/p/suse-linux-enterprise-server-12/9p32mwbh6cns">SUSE Linux Enterprise Server 12</a></li>
</ul>
</li>
 	<li><a href="https://msdn.microsoft.com/zh-tw/commandline/wsl/install_guide">官方說明文件</a></li>
</ul>
<h2>注意事項</h2>
<ul>
 	<li>當最後一個 bash.exe 結束的時候，就會把所有 WSL 的程序（包含背景執行的）結束，因此當有背景執行的 WSL 程式時，記得不要把所有的 bash.exe 都關掉。</li>
 	<li>因為 WSL 沒有 systemd，因此裝在系統上的服務不會自己開起來，要手動的打開
<code>sudo service XXX-service start</code></li>
 	<li>使用 gdb 的時候，有時候某些 libary 載入到記憶體上的位置會和正常的Linux不一樣，因此打 CTF 的 Pwn 時，在 WSL 上的結果很有可能不符合預期。</li>
 	<li>無法在上面使用虛擬化技術，因此在上面無法使用 VirtualBox 或是 kvm (qemu) 之類的東西，不過 qemu 的管理器可以使用，但只能用來管理其他電腦上的 qemu。</li>
 	<li>當你在上面開啟 port 的時候，要注意不會跟其他 Windows 程式衝到。並且當你想要讓其他人能連到的話，需要去 Windows 的防火牆中設定，讓某些 port 能夠通行。</li>
 	<li>可以在 WSL 中執行 Windows 的 exe 程式，不過檔案需要放在 Windows 的目錄中，而不是 WSL 的 Linux 目錄上。</li>
</ul>
<h2>額外的東西</h2>
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>ssh server
<ul>
 	<li>跟 Windows 10 內建的 ssh server 衝突，所以記得要改 port 或是關 Windows 10 的 ssh 功能</li>
 	<li>安裝 ssh-server
<code>sudo apt install openssh-server</code></li>
 	<li>修改【/etc/ssh/sshd_config】內的這些東西
<code>AllowUsers &lt;yourusername&gt;
PasswordAuthentication yes
UsePrivilegeSeparation no</code></li>
 	<li>然後要手動啟動 ssh-server 他
<code>sudo service ssh --full-restart</code></li>
</ul>
</li>
 	<li>圖形介面
<ul>
 	<li>我個人推薦使用的 X Server 是 <a href="https://sourceforge.net/projects/vcxsrv/">VcXsrv Windows X Server</a></li>
 	<li>安裝完之後請在啟動裡面加入有內容的 .bat 檔，讓 X Server 可以每次開機都直接啟動</li>
</ul>
<ul>
 	<li>開啟啟動資料夾可以直接【執行】【shell:startup】
<code>@echo 0
start "" "C:\Program Files\VcXsrv\vcxsrv.exe" :0 -ac -terminate -lesspointer -multiwindow -clipboard -wgl
exit</code></li>
</ul>
</li>
</ul>
</li>
 	<li>然後進到 bash 裡面，在使用者的環境變數裡面新增
<code>DISPLAY=127.0.0.1:0</code></li>
 	<li>如果使用<code>DISPLAY=:0</code>的話，本機上沒有問題，但是<code>ssh -X</code>的時候就會出現問題。</li>
 	<li>加入環境變數的方法有很多種，我個人的做法是在【~/.bashrc】裡面加入。
<code>export DISPLAY=127.0.0.1</code></li>
 	<li>Ubuntu Desktop
<ul>
 	<li>裝上這個，你就有 firefox、gnome-terminal、gedit、Libre Office 等等東西可以用了
<code>sudo apt install ubuntu-desktop</code></li>
</ul>
</li>
 	<li>Hime
<ul>
 	<li>參考<a href="http://goodjack.blogspot.tw/2013/08/linux-phonetic-setting.html">這個</a></li>
 	<li>不過有幾點要注意，第一個是要開啟【支援遠端用戶端程式】</li>
 	<li>然後在 VcXsrv 中 Ctrl-Space 這個組合鍵無法使用，因此需替換成 Shift-Space</li>
 	<li>最後記得加上以下的環境變數
<code>export GTK_IM_MODULE=hime
export QT_IM_MODULE=hime</code></li>
</ul>
</li>
 	<li>聲音
<ul>
 	<li>我個人的做法是安裝 pulse audio，然後<a href="https://github.com/kitor/wsl/raw/master/pulse6.zip">這裡</a>有別人編好的版本</li>
 	<li>然後解壓縮後執行裡面的<code>bin/pulseaudio.exe</code></li>
 	<li>接下來回到 bash 裡面，執行
<code>sudo add-apt-repository ppa:therealkenc/wsl-pulseaudio
sudo apt install --no-install-recommends libpulse0 pavucontrol</code></li>
 	<li>然後可能要寫個 script 讓他自動重啟，因為他好像一段時間沒有播放聲音就會自己關掉。</li>
 	<li>如果還有問題請參考<a href="https://github.com/Microsoft/BashOnWindows/issues/486">這裡</a></li>
</ul>
</li>
 	<li>VLC
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>這個純粹就只是為了好玩用的，不一定要裝 :P</li>
 	<li>記得確定 pulse audio 有打開喔。
<code>sudo apt install vlc</code></li>
 	<li>bash 中輸入 vlc 打開之後</li>
 	<li>【Tools】-&gt;【Video】-&gt;【Output】-&gt;【Color ASCII art video output】-&gt;【OK】</li>
 	<li>把 vlc 關掉</li>
 	<li>把環境變數的 <code>DISPLAY</code> 刪掉（或是改成空字串）</li>
 	<li>執行
<code>cvlc &lt;影片位置&gt;</code></li>
</ul>
</li>
</ul>
&nbsp;
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>欣賞漂亮的 ASCII Art 吧
<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7605"><img src="https://msdnshared.blob.core.windows.net/media/2017/09/74.png" alt="" class="aligncenter size-full wp-image-7605" width="1103" height="752" /></a></li>
</ul>
</li>
</ul>
&nbsp;</li>
 	<li>Visual Studio Code
<ul>
 	<li>在 Windows 上的 VS Code 中的設定中加入以下內容，即可將 WSL 的 bash 變成預設的 shell。
<code>"terminal.integrated.shell.windows": "C:\\WINDOWS\\Sysnative\\bash.exe",
"terminal.external.windowsExec": "C:\\WINDOWS\\Sysnative\\bash.exe",</code></li>
 	<li>但是目前沒辦法把 VS Code 裝在 WSL。有興趣可以看<a href="https://github.com/Microsoft/BashOnWindows/issues/2293">這裡</a></li>
</ul>
</li>
 	<li>PTT
<ul>
 	<li>很多人都不知道其實 PTT 可以用 ssh 的方式上去。
<code>ssh bbsu@ptt.cc</code></li>
</ul>
</li>
 	<li>安裝 WSL 在其他磁碟
<ul>
 	<li>請參考<a href="https://github.com/Microsoft/BashOnWindows/issues/449">這個</a></li>
</ul>
</li>
 	<li>改裝成 Arch
<ul>
 	<li>先<a href="https://www.archlinux.org/download">下載</a> Arch 的映像</li>
 	<li>解開後 chroot 進去 arch 的 Live CD 根目錄上</li>
 	<li>照著<a href="https://wiki.archlinux.org/index.php/installation_guide">這個安裝教學</a>安裝</li>
 	<li>裝完之後，把所有的 bash.exe 都關掉</li>
 	<li>用檔案總管進到【使用者目錄/AppData/Local/lxss】裡，找到 Arch 的根目錄，把它【移動】到最外面</li>
 	<li>把 Arch 的根目錄重新命名成 rootfs</li>
 	<li>打開 bash 測試是否成功</li>
</ul>
</li>
</ul>
&nbsp;
<h3><a href="https://blogs.msdn.microsoft.com/wsl/">WSL 技術原理</a></h3>
&nbsp;

&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7696"><img src="https://msdnshared.blob.core.windows.net/media/2017/09/Yi-Chou.png" alt="" class="size-full wp-image-7696 alignleft" width="200" height="200" /></a>

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

<span style="color: #be017a">周逸 Yi Chou</span>

Microsoft Student Partners in Taiwan
專長：C/C++、C#'PHP
GitHub:<a href="https://github.com/qazwsxedcrfvtg14">qazwsxedcrfvtg14</a>]]></content:encoded>
    </item>
    <item>
      <title>【Xamarin】Xamarin.Forms 常用 View 介紹</title>
      <link>https://docs.microsoft.com/archive/blogs/microsoft_student_partners_in_taiwan/xaformsviews</link>
      <pubDate>Mon, 02 Oct 2017 23:11:39 GMT</pubDate>
      <dc:creator><![CDATA[Microsoft Student Partners in Taiwan]]></dc:creator>
      <guid
        isPermaLink="false">http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?p=7697</guid>
      <description><![CDATA[前言：
 	Xamarin Forms 可以讓你寫一次code就可以同時製作出 Android、iOS、Windows Phone，甚至是可以在 PC、HoloLens、Xbox 上面跑的...]]></description>
      <content:encoded><![CDATA[<h4>前言：</h4>
 	Xamarin Forms 可以讓你寫一次code就可以同時製作出 Android、iOS、Windows Phone，甚至是可以在 PC、HoloLens、Xbox 上面跑的 Windows App，相當的方便！其跨平台所帶來的好處不言而喻，但同時也會造成一些問題：

<ol>
 	<li>開發環境建置門檻高
<ul>
 	<li>由於牽涉到多種平台的聯繫，等於需要對各種平台都要有相當的了解才知道怎麼設定。這個問題相較於以前已經減輕很多</li>
</ul>
</li>
 	<li>功能性
<ul>
 	<li>每個平台都有各自的功能，Xamarin Forms 只能取其交集，對於 Xamarin Forms 不提供的特定功能只能針對該平台單獨實作。問題在於你可能不了解該平台，因此不知道該如何實作</li>
 	<li>同時，身為新技術，Xamarin Forms 的確也還沒實作完「全部的交集」，某些功能在每個平台都有提供，但 Xamarin Forms 卻還未提供該功能，這時也會需要針對各個平台分別實作。這個問題已經改善很多，在未來也會持續改進</li>
</ul>
</li>
 	<li>相容性
<ul>
 	<li>每個平台皆可獨立於 Xamarin Forms 之外自行更新，但這常常造成 Xamarin Forms 對於該平台的相容性問題，建議不要隨意升級各個平台的套件，由 Xamarin Forms 統一管理其升級事宜。這個問題相較於以前已經減輕很多</li>
</ul>
</li>
</ol>
<h4>名詞解釋：</h4>
<ul>
 	<li><strong>View</strong>：通常是一個長方形的東西，有的可以在上面顯示文字、圖片、進度或任何你想顯示的東西，有的用手指點一下會有反應（「按鈕」就是一種 View），有的可以用來輸入文字</li>
 	<li><strong>Layout</strong>：是一種 View，也是一個長方形的東西，塞很多的 View 進去是它唯一的功能，但 Layout 很重要，因為沒有 Layout 你就不能在同一個畫面中放超過一個 View。Layout 通常是隱形的，你只能看到它怎麼將塞進它裡面的 View 排列在畫面上，而不能直接看到 Layout 本身</li>
</ul>
&nbsp;

p.s. 以下介紹中 Layout 會用<span style="color: #f23f3f"><i><b>斜體+粗體</b></i></span>表示

接下來就按照使用程度來介紹前 10 名小莫常用的 View 吧～

<!--more-->
<ol>
 	<li>
<h4>Button</h4>
<a href="https://4.bp.blogspot.com/-jfMEt127sbs/Wb3XZkaxFRI/AAAAAAAAJuo/Ato85DtLkygROShHVKrbcPElz9q-iNjiwCLcBGAs/s1600/Inkeduntitled_LI.jpg"><img src="https://4.bp.blogspot.com/-jfMEt127sbs/Wb3XZkaxFRI/AAAAAAAAJuo/Ato85DtLkygROShHVKrbcPElz9q-iNjiwCLcBGAs/s320/Inkeduntitled_LI.jpg" width="320" height="160" border="0" /></a>
<ul>
 	<li>這大概是最親民的 View 了吧，因為用手指戳它會有反應 XD
新手入門可以從 Button 開始玩起</li>
 	<li>Debug 的時候很好用，可以用來觸發某段測試程序</li>
 	<li>常用的屬性：Text</li>
 	<li>常用的事件：Clicked</li>
</ul>
</li>
 	<li>
<h4><em><b>StackLayout</b></em></h4>
<a href="https://2.bp.blogspot.com/-g0vmvAWtxng/Wb3Z_a4k5cI/AAAAAAAAJu0/ELewnwi-sB0rEAU-zm9o3hVyhdCDsXciQCLcBGAs/s1600/untitled2.png"><img src="https://2.bp.blogspot.com/-g0vmvAWtxng/Wb3Z_a4k5cI/AAAAAAAAJu0/ELewnwi-sB0rEAU-zm9o3hVyhdCDsXciQCLcBGAs/s320/untitled2.png" width="320" height="160" border="0" /></a>
<ul>
 	<li>想要將多個 View 放進同一個畫面，可以從 StackLayout 下手～它會幫你將 View 由上而下排得整整齊齊的 XD</li>
 	<li>常用的屬性：Children, Orientation</li>
</ul>
</li>
 	<li>
<h4><em><b>ScrollView</b></em></h4>
<a href="https://4.bp.blogspot.com/-ydx_vlQ2Umg/Wb3edhChUZI/AAAAAAAAJvA/6HT4MbzlYsUbr80GO6DpuyN7aW5Q1E7jgCLcBGAs/s1600/untitled3.png"><img src="https://4.bp.blogspot.com/-ydx_vlQ2Umg/Wb3edhChUZI/AAAAAAAAJvA/6HT4MbzlYsUbr80GO6DpuyN7aW5Q1E7jgCLcBGAs/s320/untitled3.png" width="320" height="160" border="0" /></a>
<ul>
 	<li>當一個 View 太大，比一個螢幕的大小還大，要怎麼辦呢？讓它可以用手指滑動是一個好選擇，就把它塞進 ScrollView 裡面吧～ ScrollView 是一個比較特別的 Layout，它只能塞一個 View 進去</li>
 	<li>常用的屬性：Content</li>
</ul>
</li>
 	<li>
<h4>Editor</h4>
<a href="https://1.bp.blogspot.com/-5tJ82H12GU0/Wb3gODVuflI/AAAAAAAAJvM/PzgMcWkWw1IOb1xZtP_gbhclmPRmZuaUgCLcBGAs/s1600/Inkeduntitled4_LI.jpg"><img src="https://1.bp.blogspot.com/-5tJ82H12GU0/Wb3gODVuflI/AAAAAAAAJvM/PzgMcWkWw1IOb1xZtP_gbhclmPRmZuaUgCLcBGAs/s320/Inkeduntitled4_LI.jpg" width="320" height="160" border="0" /></a>
<ul>
 	<li>想要一個可以打字的地方嗎？那就非 Editor 莫屬了！</li>
 	<li>常用的屬性：Text</li>
</ul>
</li>
 	<li>
<h4>Image</h4>
<a href="https://1.bp.blogspot.com/-9ntNr6ewBLY/Wb3hUgPKCII/AAAAAAAAJvY/ycHy0iiVErUqY2zANeV03SL5Bq4AY2qxwCLcBGAs/s1600/Inkeduntitled5_LI.jpg"><img src="https://1.bp.blogspot.com/-9ntNr6ewBLY/Wb3hUgPKCII/AAAAAAAAJvY/ycHy0iiVErUqY2zANeV03SL5Bq4AY2qxwCLcBGAs/s320/Inkeduntitled5_LI.jpg" width="320" height="160" border="0" /></a>
<ul>
 	<li>想要顯示一張圖片嗎？那就非 Image 莫屬了！</li>
 	<li>常用的屬性：Source</li>
</ul>
</li>
 	<li>
<h4>ActivityIndicator</h4>
<a href="https://4.bp.blogspot.com/-Wdh-ElV7IQk/Wb3iL6eZUqI/AAAAAAAAJvk/-yRo8zrJk7QxEHO87nsfwL3Ch1oeuMoCwCLcBGAs/s1600/Inkeduntitled6_LI.jpg"><img src="https://4.bp.blogspot.com/-Wdh-ElV7IQk/Wb3iL6eZUqI/AAAAAAAAJvk/-yRo8zrJk7QxEHO87nsfwL3Ch1oeuMoCwCLcBGAs/s320/Inkeduntitled6_LI.jpg" width="320" height="160" border="0" /></a>
<ul>
 	<li>如果程式正在下載一個檔案，或者進行一個龐大的計算，要很久，怎麼辦？顯示一個圈圈轉轉轉，或者一堆點點跑跑跑，讓使用者更心煩（X）紓壓（O）吧～</li>
 	<li>常用的屬性：IsRunning</li>
</ul>
</li>
 	<li>
<h4>Label</h4>
<a href="https://3.bp.blogspot.com/-OSDZ2ioer8o/Wb3l22lFdrI/AAAAAAAAJvw/BN3iJPwxPYYN3-3d0mcEOGJOdLzVaXw-QCLcBGAs/s1600/Inkeduntitled7_LI.jpg"><img src="https://3.bp.blogspot.com/-OSDZ2ioer8o/Wb3l22lFdrI/AAAAAAAAJvw/BN3iJPwxPYYN3-3d0mcEOGJOdLzVaXw-QCLcBGAs/s320/Inkeduntitled7_LI.jpg" width="320" height="160" border="0" /></a>
<ul>
 	<li>想要顯示文字？不想像 Button 一樣被戳會有反應，也不想像 Editor 一樣可以編輯文字。哦，那就用 Label 吧！除了用來顯示文字之外，甚麼事都不能做！XD</li>
 	<li>常用的屬性：Text</li>
</li>
</ul>
</li>
 	<li>
<h4>Entry</h4>
<a href="https://3.bp.blogspot.com/-kWk_qb8nj8M/Wb3neSi_uMI/AAAAAAAAJv8/6rHVrCxl-DYiekHbvJinwt-cJcpoSARqwCLcBGAs/s1600/Inkeduntitled8_LI.jpg"><img src="https://3.bp.blogspot.com/-kWk_qb8nj8M/Wb3neSi_uMI/AAAAAAAAJv8/6rHVrCxl-DYiekHbvJinwt-cJcpoSARqwCLcBGAs/s320/Inkeduntitled8_LI.jpg" width="320" height="160" border="0" /></a>
<ul>
 	<li>這是一個只能輸入一行的 Editor，這有甚麼用？方便的很呢，這樣使用者平常按換行（Enter）的那個鍵就可以用來當作「輸入完畢」鍵了！</li>
 	<li>常用的屬性：Text, Placeholder, IsPassword</li>
 	<li>常用的事件：Completed</li>
</ul>
</li>
 	<li>
<h4><em><b>Grid</b></em></h4>
<a href="https://2.bp.blogspot.com/-k8Cqa-jizX0/Wb3orpXGh8I/AAAAAAAAJwI/Mlf83ZTTMXQkJObufw3cqvHpx7pzyw-iwCLcBGAs/s1600/untitled9.png"><img src="https://2.bp.blogspot.com/-k8Cqa-jizX0/Wb3orpXGh8I/AAAAAAAAJwI/Mlf83ZTTMXQkJObufw3cqvHpx7pzyw-iwCLcBGAs/s320/untitled9.png" width="320" height="160" border="0" /></a>
<ul>
 	<li>要怎麼把 View 排成九宮格呀？看看那邊～ Grid 正在對你偷笑！^_^
除了做出九宮格之外，Grid 的強大排版功能可不只你想的那樣！</li>
 	<li>警告：<b>GridLength.Auto 不可濫用</b>，100 個 Auto 會拖慢你的 App 至少 0.5 秒！</li>
 	<li>常用的屬性：RowDefinitions, ColumnDefinitions</li>
</ul>
</li>
 	<li>
<h4>ProgressBar</h4>
<a href="https://3.bp.blogspot.com/-6BmV8Dk_3jw/Wb3r01bLJHI/AAAAAAAAJwU/cQZxsoHFkjceUcfEBdYsxyu3XiECD2cKgCLcBGAs/s1600/Inkeduntitled10_LI.jpg"><img src="https://3.bp.blogspot.com/-6BmV8Dk_3jw/Wb3r01bLJHI/AAAAAAAAJwU/cQZxsoHFkjceUcfEBdYsxyu3XiECD2cKgCLcBGAs/s320/Inkeduntitled10_LI.jpg" width="320" height="160" border="0" /></a>
<ul>
 	<li>ProgressBar 就是進度條，使用時機和 ActivityIndicator 類似，但是多了「顯示進度」的功能</li>
 	<li>說的容易做起來難，你在寫程式的時候要怎麼讓程式知道自己跑了幾趴（%）？OwO
各顯神通吧　XD</li>
 	<li>常用的屬性：Progress</li>
 	<li>常用的方法：ProgressTo</li>
</ul>
</li>
</ol>

&nbsp;
&nbsp;

部落格原文：<a href="http://codingsimplifylife.blogspot.tw/2017/09/xamarin-forms-view.html">Xamarin Forms 常用 View 的介紹</a>
更多View的介紹請見：<a href="https://developer.xamarin.com/guides/xamarin-forms/user-interface/controls/">Xamarin Forms 官方網站（全英文）</a>～
<span style="font-size: xx-small">圖片來源：<a href="https://developer.xamarin.com/guides/xamarin-forms/user-interface/controls/views/">Views</a>、</span><a href="https://developer.xamarin.com/guides/xamarin-forms/user-interface/controls/layouts/"><span style="font-size: xx-small">Layouts</span>
</a>

&nbsp;
&nbsp;
&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7707"><img src="https://msdnshared.blob.core.windows.net/media/2017/09/Bo-Hsu-Yu.png" alt="" class="size-full wp-image-7707 alignleft" width="200" height="200" /></a>
&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;
&nbsp;
&nbsp;


<span style="color: #b9027d">余柏序 Bo-Hsu Yu</span>

專長：<span>C++、C#、JavaScript、Python、Shell Script</span>

GitHub：<a href="https://github.com/fsps60312">fsps60312</a>

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Microsoft Imagine 開通序號的使用方法！</title>
      <link>https://docs.microsoft.com/archive/blogs/microsoft_student_partners_in_taiwan/msimaginecode</link>
      <pubDate>Fri, 14 Jul 2017 12:31:55 GMT</pubDate>
      <dc:creator><![CDATA[Microsoft Student Partners in Taiwan]]></dc:creator>
      <guid
        isPermaLink="false">http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?p=7406</guid>
      <description><![CDATA[哈囉大家好！

相信大家都知道 Microsoft Imagine 這個專屬於學生的開發者訂閱了吧！

在 MSP 舉辦的工作坊中以及像是黑客松的活動中，我們都會提供一組 Imagine...]]></description>
      <content:encoded><![CDATA[哈囉大家好！

相信大家都知道 Microsoft Imagine 這個專屬於學生的開發者訂閱了吧！

在 MSP 舉辦的工作坊中以及像是黑客松的活動中，我們都會提供一組 Imagine 的註冊碼來供參與的學生使用，那麼這組序號要如何使用呢？本篇文章就要來告訴大家！

<!--more-->

<strong>第一步：</strong>登入 <a href="https://imagine.microsoft.com/zh-tw">Microsoft Imagine</a>！

（只要註冊為 Microsoft 的帳號即可！可以利用 GitHub、Google 等帳號註冊）

&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7455"><img src="https://msdnshared.blob.core.windows.net/media/2017/07/012.png" alt="" class="aligncenter wp-image-7455" width="760" height="592" /></a>

&nbsp;

<strong>第二步：</strong>完成個人資訊

藉由右上角的登入按鈕登入你所使用的 Microsoft 帳號之後，接著請點選導覽列中的「帳戶」，會跳至以下畫面，請將該頁面中的個人資訊填寫完畢之後，按下接受。

&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7465"><img src="https://msdnshared.blob.core.windows.net/media/2017/07/023.png" alt="" class="aligncenter wp-image-7465" width="760" height="590" /></a>

&nbsp;

<a id="3"></a><strong>第三步</strong>

完成了個人資訊的填寫，會被導向至下方圖片的頁面，可以看到左方的「帳號狀態」下面有一個可以點選的文字：請驗證您的學生身分。

&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7475"><img src="https://msdnshared.blob.core.windows.net/media/2017/07/032.png" alt="" class="aligncenter size-full wp-image-7475" width="760" /></a>

&nbsp;

<strong>第四步：</strong>驗證學生身分

這個步驟是本篇文章中最重要的部分，再點選前一步驟中的連結之後，會被導向到一個詢問你要如何驗證身分的網頁，這時候請點選下拉式選單，並選取「Microsoft Imagine 驗證碼」的選項，如下：

&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7476"><img src="https://msdnshared.blob.core.windows.net/media/2017/07/043.png" alt="" class="aligncenter size-full wp-image-7476" width="760" /></a>

&nbsp;

接著會出現一個要求輸入驗證碼的網頁，這時候不要懷疑！趕快拿起你從 MSP 手中取得的 Microsoft Imagine 驗證碼，然後依照順序輸入進去吧！

&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7485"><img src="https://msdnshared.blob.core.windows.net/media/2017/07/053.png" alt="" class="aligncenter wp-image-7485" width="760" height="590" /></a>

&nbsp;

<strong>提醒：</strong>成功輸入完之後會出現一個很大的「恭喜」。

&nbsp;

<strong>第五步：</strong>確認帳戶狀態

輸入完序號，也成功看到系統的祝賀之後，請回到<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/07/14/msimaginecode/#3">步驟三</a>的頁面，這時候應該會發現自己的帳戶狀態已經從要求驗證學生身分，轉變成「<strong><span style="color: #339966">已驗證。到期日：xx/xx/xxxx</span></strong>」的綠色文字。

&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7415"><img src="https://msdnshared.blob.core.windows.net/media/2017/07/07.png" alt="" class="aligncenter size-full wp-image-7415" width="760" /></a>

&nbsp;

<strong>第六步：</strong>開通 Azure 學生訂閱！

<span style="color: #ff0000">※注意：</span>請稍待約一分鐘之後再進行操作，不然有可能會跳出無法使用該訂閱的錯誤訊息。

接續著上一個步驟的畫面，確認完學生身分之後，就可以按下下方的「<strong>啟用 Azure &gt;</strong>」按鈕，然後按下「<strong>立即登入</strong>」來完成 Azure 的訂閱。

&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7425"><img src="https://msdnshared.blob.core.windows.net/media/2017/07/08.png" alt="" class="aligncenter size-full wp-image-7425" width="760" /></a>

&nbsp;

<span style="color: #ff0000">※注意：</span>如果有跳出要求登入的畫面，請登入與申請 Microsoft Imagine <strong>相同</strong>的帳號。

若無意外，應該會看到以下要求註冊的畫面，請按照要求填入手機號碼，並取得驗證碼。

&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7505"><img src="https://msdnshared.blob.core.windows.net/media/2017/07/Inked09_LI.jpg" alt="" class="aligncenter size-full wp-image-7505" width="760" /></a>

&nbsp;

見到以下畫面，就完、工、了！

&nbsp;

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=7435"><img src="https://msdnshared.blob.core.windows.net/media/2017/07/104.png" alt="" class="aligncenter size-full wp-image-7435" width="760" /></a>

&nbsp;

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>【Node JS】Node js 實作課程：小遊戲製作</title>
      <link>https://docs.microsoft.com/archive/blogs/microsoft_student_partners_in_taiwan/nodejs-3</link>
      <pubDate>Wed, 07 Jun 2017 14:02:17 GMT</pubDate>
      <dc:creator><![CDATA[Microsoft Student Partners in Taiwan]]></dc:creator>
      <guid
        isPermaLink="false">http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?p=7325</guid>
      <description><![CDATA[0429 Facebook 小遊戲簡介這次的專案使用到了 Facebook Graph API，還不熟悉的讀者，可以參考我們先前的文章，或是到 Facebook...]]></description>
      <content:encoded><![CDATA[<div id="doc" class="markdown-body container-fluid"><h1 id="0429-facebook-小遊戲">0429 Facebook 小遊戲</h1><h2 id="簡介">簡介</h2><p>這次的專案使用到了 Facebook Graph API，還不熟悉的讀者，可以參考我們先前的<a href="https://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/06/nodejs/" target="_blank">文章</a>，或是到 <a href="https://developers.facebook.com/docs/graph-api" target="_blank">Facebook 開發人員中心</a>查看說明。</p><h3 id="websocket">WebSocket</h3><p>在這個範例中，我們使用了 WebSocket 作為伺服器和瀏覽器的溝通方式，它可以讓我們維持連線，不像一般讀取網頁一樣送完資料就結束連線，這剛好符合了我們的需求(我們必須不斷傳送題目、接收答案、公布解答等等)<!--more-->

，有關這個技術的詳細資訊在網路上已經有很多解說，在開始專案之前，建議讀者可以先去看看。</p><h3 id="線上範例">線上範例</h3><p>這次的範例我們特別放到 Azure 上展示，實作前不妨先看看成果，讓你更有看到結尾的決心吧。<br>
<a href="http://nodec4.azurewebsites.net/" target="_blank">http://nodec4.azurewebsites.net/</a></p><h2 id="第一部分－配對">第一部分－配對</h2><p>第一部分我們先來做配對的部分，我們給第一位玩家專用的 URL ，等到第二位玩家存取這個網址後，檢查好友關係，並送出訊息讓網頁導向遊戲畫面。</p><h3 id="流程圖">流程圖</h3><p>為了方便，我們在每次的通訊都會加上 <strong>type</strong> 這個值，用來說明這次的通訊目的，下圖中左、右方的文字就是 type 。</p><p><img src="https://msdnshared.blob.core.windows.net/media/2017/05/1.svg" alt=""></p><ul>
<li>第一位玩家必須由<strong>1.建立遊戲</strong>來取得遊戲ID，並讓自己加入。</li>
<li>第二位玩家則需要提供遊戲ID來加入。</li>
<li>兩個人都加入後，伺服器會發送 <strong>MATCH</strong> 訊息給所有人。</li>
</ul><h3 id="登入流程">登入流程</h3><p>Facebook 的登入流程相信大家已經很熟悉了，這裡就不多做解釋，程式部分如下。</p><pre><code class="javascript hljs">app.get(<span class="hljs-string">'/api/code'</span>, (req, res) =&gt; {
    request(<span class="hljs-string">'https://graph.facebook.com/v2.8/oauth/access_token?client_id='</span> + process.env.appID + <span class="hljs-string">'&amp;redirect_uri='</span> + process.env.redirect + <span class="hljs-string">'/api/code?id='</span> + req.query.id + <span class="hljs-string">'&amp;client_secret='</span> + process.env.appKEY + <span class="hljs-string">'&amp;code='</span> + req.query.code, (error, response, body) =&gt; {
        
        <span class="hljs-keyword">var</span> userdata = <span class="hljs-built_in">JSON</span>.parse(body);
        req.session.key = userdata.access_token;
        <span class="hljs-comment">/*  */</span>
        getUser(userdata.access_token).then(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
            req.session.name = data.name;
            req.session.fbid = data.id;
			<span class="hljs-comment">// #27</span>
            <span class="hljs-keyword">var</span> url = (req.query.id != <span class="hljs-string">'undefined'</span>)? <span class="hljs-string">'../?id='</span> + req.query.id : <span class="hljs-string">'../'</span>;
            res.redirect(url);
        });
    });
});
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUser</span>(<span class="hljs-params">key</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
        request(<span class="hljs-string">'https://graph.facebook.com/v2.8/me?fields=id%2Cname&amp;access_token='</span> + key, (error, response, body) =&gt; {
            resolve(<span class="hljs-built_in">JSON</span>.parse(body));
        });
    });
}
</code></pre><blockquote style="border-left-color: blue">
<p>在 27 行我們判斷網址中是不是有 id 這個值，若有的話我們得一起傳到下個網頁去，否則使用者登入之後會遺失遊戲 id 。<br>
<span class="color"></span></p>
</blockquote><h3 id="websocket-中如何使用-session">WebSocket 中如何使用 Session</h3><p>還記得接收到 Code 之後我們做的事情嗎? 我們把使用者的 id,name,access_token 存到 Session 中方便後續使用，這時候就會有個小問題出現囉，WebSocket (下方簡稱WS) 收到訊息後，是沒有辦法直接存取到 Session 的。但我們可以使用 SessionParser 取得資料內容後傳給 WS，方法有很多種，程式碼如下</p><pre><code class="javascript hljs"><span class="hljs-keyword">const</span> wss = <span class="hljs-keyword">new</span> WebSocket.Server({ server, <span class="hljs-attr">path</span>: <span class="hljs-string">"/ws"</span> });
wss.on(<span class="hljs-string">'connection'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">connection</span>(<span class="hljs-params">ws</span>) </span>{
    sessionParser(ws.upgradeReq, {}, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">var</span> session = ws.upgradeReq.session;
        ws.session = session;
    });
    ws.on(<span class="hljs-string">'message'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">incoming</span>(<span class="hljs-params">data</span>) </span>{
       <span class="hljs-comment">// 處理接收到的訊息</span>
    });
});
</code></pre><p>我們在 WS 連接 (on connection) 時，存取 session ，並把讀取到的資料傳給 ws.session 中。</p><blockquote style="border-left-color: red">
<p>我們是在 WS 連接時存取資料，所以之後若 session 有更新，已連線的 WS 是沒有辦法獲得新資料的，但在這個範例我們不在乎這點，因為我們要的資料只有 Facebook 資訊，這些資料早在 WS 連接前就寫入了。<br>
<span class="color"></span></p>
</blockquote><h3 id="module">Module</h3><p>在前幾個範例中，我們把所有的程式都寫在 <mark>app.js</mark> 中，這不是件好事情，這次我們分成 4 個檔案。</p><table>
<thead>
<tr>
<th>檔案名稱</th>
<th>說明</th>
</tr>
</thead>
<tbody>
<tr>
<td>app.js</td>
<td>程式進入點，開啟伺服器，route設定</td>
</tr>
<tr>
<td>wsConnect.js</td>
<td>處理 WebSocket 通訊(接收和傳送)</td>
</tr>
<tr>
<td>gameManager.js</td>
<td>遊戲流程，建立遊戲房間，要求題目，評分</td>
</tr>
<tr>
<td>question.js</td>
<td>負責出題目</td>
</tr>
</tbody>
</table><p>這麼一來程式架構就比較清楚了，那不同檔案之間要怎麼溝通呢?<br>
我們來看看下面這個例子，1.js 中呼叫 2.js 的 Function<br>
<strong>1.js</strong></p><pre><code class="javascript hljs"><span class="hljs-keyword">var</span> secondJs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./2'</span>)
secondJs.hello();
</code></pre><p><strong>2.js</strong></p><pre><code class="javascript hljs"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">printHelloWord</span>(<span class="hljs-params"></span>)</span>{
	<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello Word"</span>);
}
<span class="hljs-built_in">module</span>.exports = {
	<span class="hljs-attr">hello</span>: printHelloWord
}
</code></pre><p>4 ~ 6 行中，我們定義了 2.js 的輸出，這麼一來，在 1.js 中呼叫  secondJs.hell 就會對應到 2.js 的 printHelloWord 了。<br>
在接下來的程式中，為了方便，我們會將輸出的名稱和真正的名稱設為相同，像這樣</p><pre><code class="javascript hljs"><span class="hljs-built_in">module</span>.exports = {
	<span class="hljs-attr">printHelloWord</span>: printHelloWord
}
<span class="hljs-comment">// 名稱相同時，我們可以簡寫成下面這個樣子</span>
<span class="hljs-built_in">module</span>.exports = {
	printHelloWord
}
</code></pre><h3 id="後端程式">後端程式</h3><h4 id="建立-websocket-伺服器">建立 WebSocket 伺服器</h4><p>在這個範例中，我們會使用 Express 傳送靜態檔案，使用 WebSocket 來進行資料傳輸，這邊主要講解 WS 的部分。</p><pre><code class="javascript hljs"><span class="hljs-comment">//#41</span>
<span class="hljs-keyword">const</span> server = http.createServer(app);
<span class="hljs-comment">//#42</span>
<span class="hljs-keyword">const</span> wss = <span class="hljs-keyword">new</span> WebSocket.Server({ server, <span class="hljs-attr">path</span>: <span class="hljs-string">"/ws"</span> });
wss.on(<span class="hljs-string">'connection'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">connection</span>(<span class="hljs-params">ws</span>) </span>{
    sessionParser(ws.upgradeReq, {}, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">var</span> session = ws.upgradeReq.session;
        ws.session = session;
    });
	<span class="hljs-comment">//#48</span>
    ws.on(<span class="hljs-string">'message'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">incoming</span>(<span class="hljs-params">data</span>) </span>{
        <span class="hljs-keyword">var</span> msg = { <span class="hljs-attr">type</span>: <span class="hljs-literal">null</span> };
        <span class="hljs-keyword">try</span> {
            msg = <span class="hljs-built_in">JSON</span>.parse(data);
        } <span class="hljs-keyword">catch</span> (e) { }
        <span class="hljs-keyword">if</span> (msg.type == <span class="hljs-literal">null</span>) {
            ws.close();
        }
        <span class="hljs-keyword">else</span> {
            wsc.route(ws, msg);
        }

    });
});
</code></pre><p>41 行 createServer(app)：載入 Express 的設定<br>
42 行 WebSocket.Server：在路徑 /ws 下建立 WS 伺服器，我們使用 <a href="https://github.com/websockets/ws" target="_blank">ws</a> 模組。</p><pre><code class="javascript hljs"><span class="hljs-keyword">var</span> WebSocket = <span class="hljs-built_in">require</span>(<span class="hljs-string">'ws'</span>);
</code></pre><p>48 ws.on(‘message’…)：行定義了接收到訊息的動作，前面有提到為了方便，我們每次通訊都會加上 type 屬性，在接收到訊息後我們先檢查這個屬性，關閉不相容的通訊，接把訊息交給 <strong>wsConnection.js</strong> 處理，當然我們也先引用我們自己的模組。</p><pre><code class="javascript hljs"><span class="hljs-keyword">var</span> wsc = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./wsConnect'</span>);
</code></pre><blockquote style="border-left-color: blue">
<p>若您使用 Azure Web Service 作為平台，要先到設定面板中開啟 Websocket 功能才可以使用。</p>
<p>Azure Web Service 會限制 WebSocket 的連線數量，詳細資訊可以在<a href="https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits" target="_blank">這裡</a>找到，免費的方案只有 5 個連線數，所以關閉不使用的連線是個聰明的選擇。<br>
<span class="color"></span></p>
</blockquote><h4 id="下載檔案">下載檔案</h4><p>前面有提到，關於遊戲管理的部分，都由 <mark>gameManager.js</mark> 處理，我們沒有辦法在這裡教學這部分，麻煩讀者到 <a href="https://raw.githubusercontent.com/oscar60310/NodeJsClass4/master/gameManager.js" target="_blank">GitHub</a> 下載這個檔案，並放在和 app.js 同一個目錄的位置。</p><h4 id="處理通訊">處理通訊</h4><p>首先，我們得先引用 gameManager ，並定義好 route 函式(用來處理接收到的訊息)以及輸出，另外我們 WS 訊息會以 json 格式傳送，為了方便，我們先寫好傳送 JSON 的功能。</p><pre><code class="javascript hljs"><span class="hljs-keyword">const</span> gm = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./gameManager'</span>);
route = <span class="hljs-function">(<span class="hljs-params">ws, msg</span>) =&gt;</span> {
    <span class="hljs-keyword">switch</span> (msg.type) {
		<span class="hljs-comment">//依照不同類別處理訊息</span>
	}
}
<span class="hljs-comment">// 傳送JSON格式資料</span>
sendJson = <span class="hljs-function">(<span class="hljs-params">ws, msg</span>) =&gt;</span> {
    ws.send(<span class="hljs-built_in">JSON</span>.stringify(msg));
}
<span class="hljs-comment">// 定義輸出</span>
<span class="hljs-built_in">module</span>.exports = {
    route
}
</code></pre><p>在配對的頁面中，我們會接到三種訊息，分別是 <strong>LOGIN</strong> 檢查登入狀態、<strong>CREATE</strong> 建立遊戲房間以及 <strong>JOIN</strong> 加入遊戲。</p><p>檢查登入狀態和以前相通，有登入就回傳名子，沒有就給授權網址，指示改為 WS 發送而已。但在回呼網址中，我們加上了 id 參數，這是為了讓有遊戲 ID 但還未登入的使用者保留 ID，而不會在登入後(網頁重新導向)遺失這個參數。</p><pre><code class="javascript hljs"><span class="hljs-keyword">case</span> <span class="hljs-string">"LOGIN"</span>:
<span class="hljs-keyword">if</span> (ws.session.name)
	sendJson(ws, { <span class="hljs-attr">type</span>: msg.type, <span class="hljs-attr">status</span>: <span class="hljs-string">'ok'</span>, <span class="hljs-attr">name</span>: ws.session.name });
<span class="hljs-keyword">else</span>
	sendJson(ws, { <span class="hljs-attr">type</span>: msg.type, <span class="hljs-attr">status</span>: <span class="hljs-string">'not login'</span>, <span class="hljs-attr">url</span>: <span class="hljs-string">'https://www.facebook.com/v2.8/dialog/oauth?client_id='</span> + process.env.appID + <span class="hljs-string">'&amp;redirect_uri='</span> + process.env.redirect + <span class="hljs-string">'/api/code?id='</span> + msg.game + <span class="hljs-string">'&amp;scope=user_friends,user_birthday,user_photos'</span> });
<span class="hljs-keyword">break</span>;
</code></pre><p>接著是建立遊戲 ID 的部分了，用法很簡單，呼叫 createNewGame 即可</p><pre><code class="javascript hljs"><span class="hljs-keyword">case</span> <span class="hljs-string">"CREATE"</span>:
<span class="hljs-keyword">if</span> (ws.session.name)
	sendJson(ws, { <span class="hljs-attr">type</span>: msg.type, <span class="hljs-attr">status</span>: <span class="hljs-string">'ok'</span>, <span class="hljs-attr">id</span>: gm.createNewGame(ws), <span class="hljs-attr">name</span>: ws.session.name });
<span class="hljs-keyword">else</span>
	sendJson(ws, { <span class="hljs-attr">type</span>: msg.type, <span class="hljs-attr">status</span>: <span class="hljs-string">"not login"</span> });
<span class="hljs-keyword">break</span>;
</code></pre><blockquote style="border-left-color: pink">
<p>建立的遊戲 ID 在十分鐘後會自動刪除<br>
<span class="color"></span></p>
</blockquote><p>最後是加入遊戲的部分了，值得注意的是兩個人(不管是不是建立遊戲的人)都需要執行才能開始遊戲。<br>
用法為 joinGame([Websocket],[Game ID]) ，為 promise，會回傳 status (是否成功加入) 及 todo，若 todo 為 <mark>notify</mark> 時，表示已經有兩個人加入，可以開始遊戲，這時候我們必須傳送 MATCH 指令到前端。</p><pre><code class="javascript hljs"><span class="hljs-keyword">case</span> <span class="hljs-string">"JOIN"</span>:
<span class="hljs-keyword">if</span> (msg.game &amp;&amp; ws.session.name) {
	gm.joinGame(ws, msg.game).then(<span class="hljs-function">(<span class="hljs-params">d</span>) =&gt;</span> {
		ws.session.game = msg.game;
		sendJson(ws, { <span class="hljs-attr">type</span>: msg.type, <span class="hljs-attr">status</span>: d.status, <span class="hljs-attr">id</span>: msg.game });
		<span class="hljs-keyword">if</span> (d.todo == <span class="hljs-string">'notify'</span>) {
			d.notify.forEach(<span class="hljs-function">(<span class="hljs-params">wsToNotify</span>) =&gt;</span> {
			sendJson(wsToNotify, { <span class="hljs-attr">type</span>: <span class="hljs-string">"MATCH"</span>, <span class="hljs-attr">players</span>: d.players });
			})
		}
	});
} <span class="hljs-keyword">else</span> {
	sendJson(ws, { <span class="hljs-attr">type</span>: msg.type, <span class="hljs-attr">status</span>: <span class="hljs-string">"not login or no game id"</span> });
}
<span class="hljs-keyword">break</span>;
</code></pre><h3 id="前端程式">前端程式</h3><p>因為這次很多和前端的互動，我們得知道前後端怎麼運作的，所以這次我們會講解一些前端的東西。</p><h4 id="開啟-websocket">開啟 WebSocket</h4><p>前端開啟 ws 很簡單</p><pre><code class="javascript hljs"><span class="hljs-keyword">new</span> WebSocket(<span class="hljs-string">"ws://[url]"</span>);
</code></pre><p>但考慮到我們可能在 HTTPS 連線下使用(這時須使用 wss://)，所以我們把程式改寫成這樣</p><pre><code class="javascript hljs"><span class="hljs-keyword">var</span> protocol = (<span class="hljs-built_in">window</span>.location.protocol == <span class="hljs-string">"https:"</span>) ? <span class="hljs-string">"wss://"</span> : <span class="hljs-string">"ws://"</span>;
ws = <span class="hljs-keyword">new</span> WebSocket(protocol + <span class="hljs-built_in">window</span>.location.host + <span class="hljs-string">"/ws"</span>);
</code></pre><p>後面的 /ws 是我們在伺服器設定的路徑<br>
接著我們定義 onopen(開啟連線)、onmessage(收到訊息)、onclose(關閉連線)的動作</p><pre><code class="javascript hljs">self.ws.onopen = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        <span class="hljs-comment">// Web Socket is connected, send data using send()</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"[ws] connected."</span>);
        self.sendJson(self.ws, {
            <span class="hljs-attr">type</span>: <span class="hljs-string">"LOGIN"</span>
        });
};
self.ws.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">evt</span>) </span>{
    <span class="hljs-keyword">var</span> msg = <span class="hljs-built_in">JSON</span>.parse(evt.data);
    <span class="hljs-built_in">console</span>.log(msg);
    <span class="hljs-keyword">switch</span> (msg.type) {
       <span class="hljs-keyword">case</span> <span class="hljs-string">"LOGIN"</span>:
            loginStatus(msg);
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">"CREATE"</span>:
            newgame(msg);
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">"JOIN"</span>:
            checkJoin(msg);
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">"MATCH"</span>:
            match(msg);
            <span class="hljs-keyword">break</span>;
    }

};
<span class="hljs-keyword">this</span>.ws.onclose = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"ws closed."</span>);
};
<span class="hljs-keyword">this</span>.sendJson = <span class="hljs-function">(<span class="hljs-params">ws, msg</span>) =&gt;</span> {
    ws.send(<span class="hljs-built_in">JSON</span>.stringify(msg));
}
</code></pre><p>首先，我們一樣定義了 sendJson 來傳送資料，我們把所有的內容包在 <strong>WsManager</strong> 中，方便我們使用，並且在最上方定義了 self = WsManager，這只是我們的習慣，讀者可以自行決定。</p><p>建立完成連線後，我們馬上檢查登入，並等待伺服器回傳資訊。登入之後，我們先試試看能不能利用 ID 加入遊戲</p><pre><code class="javascript hljs">loginStatus = <span class="hljs-function">(<span class="hljs-params">msg, text, url</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (msg.status == <span class="hljs-string">"ok"</span>) {
            self.sendJson(self.ws, {
                <span class="hljs-attr">type</span>: <span class="hljs-string">"JOIN"</span>,
                <span class="hljs-attr">game</span>: getUrlParameter(<span class="hljs-string">"id"</span>)
            });
        } <span class="hljs-keyword">else</span> {
            setAlert(<span class="hljs-string">"&lt;a href='"</span> + msg.url + <span class="hljs-string">"' class=\"btn btn-primary btn-lg\" role=button&gt;玩家FB登入&lt;/a&gt;"</span>);
        }
}
</code></pre><p>若沒辦法加入，我們自己建立一個</p><pre><code class="javascript hljs">checkJoin = <span class="hljs-function">(<span class="hljs-params">msg</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (msg.status != <span class="hljs-string">"ok"</span>) {
            setAlert(<span class="hljs-string">"無法加入"</span>, <span class="hljs-string">"ID 無效，產生新的房間..."</span>, <span class="hljs-string">""</span>);
            setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> self.sendJson(self.ws, {
                <span class="hljs-attr">type</span>: <span class="hljs-string">"CREATE"</span>
            }), <span class="hljs-number">1000</span>);
        } <span class="hljs-keyword">else</span>
            self.gid = msg.id;
    }
</code></pre><p>加入完成後，就等待伺服器回傳 MATCH 指令就可以了！<br>
這部分的完整程式在<a href="https://github.com/oscar60310/NodeJsClass4/blob/master/static/js/Match_web.js" target="_blank">這裡</a></p><h3 id="小節">小節</h3><p>到這裡我們已經完成了配對，接著就遊戲的內容了，配對完成後，前端要把兩位使用者導向 room.html 這個網頁，並附上遊戲 ID<br>
<img src="https://i.imgur.com/Rch44n1.png" alt=""><br>
原諒我們沒有時間美化介面^^</p><blockquote style="border-left-color: pink">
<p>配對的流程需要兩個人(廢話)，若您只有一個人…可以開兩個瀏覽器或兩頁分頁<br>
<span class="color"></span></p>
</blockquote><h2 id="第二部分－遊戲">第二部分－遊戲</h2><h3 id="流程圖1">流程圖</h3><p><img src="https://msdnshared.blob.core.windows.net/media/2017/05/2.svg" alt=""></p><ol>
<li>使用者加入後會傳送 INFO 要求取得遊戲資訊(兩位玩家的名稱和照片)</li>
<li>當兩位玩家都傳送要求後，伺服器會開始製作題目，製作完成時發送 COMPUTING (finish: true)</li>
<li>接著前端必須發送 READY 要求(當使用者按下<strong>準備</strong>)</li>
<li>伺服器會發送 START 訊息，五秒後會發送題目</li>
<li>伺服器發送題目，前端回傳答案，伺服器公布結果，重複執行值到沒有題目</li>
<li>結束遊戲</li>
</ol><h3 id="取得題目">取得題目</h3><h4 id="info">INFO</h4><p>取得遊戲資訊，使用方式很為 info([WebSocket],[Game ID])<br>
回傳 data (遊戲資訊內容)、todo (當todo為notify時，表示有兩位玩家要求 info ，開始準備題目)</p><pre><code class="javascript hljs"><span class="hljs-keyword">case</span> <span class="hljs-string">"INFO"</span>:
<span class="hljs-keyword">if</span> (ws.session.name &amp;&amp; msg.game) {
	gm.info(ws, msg.game).then(<span class="hljs-function">(<span class="hljs-params">d</span>) =&gt;</span> {
		d.data.type = msg.type;
		ws.gameid = msg.game;
		sendJson(ws, d.data);
		<span class="hljs-keyword">if</span> (d.todo == <span class="hljs-string">"notify"</span>) {
			sendJson(d.ws[<span class="hljs-number">0</span>], { <span class="hljs-attr">type</span>: <span class="hljs-string">"COMPUTING"</span> });
     			sendJson(d.ws[<span class="hljs-number">1</span>], { <span class="hljs-attr">type</span>: <span class="hljs-string">"COMPUTING"</span> });
			gm.createQuestions(msg.game).then(notifyGetReady);
		}
	});
}
<span class="hljs-keyword">break</span>;
</code></pre><p>題目開始準備和準備完成我們都會發送訊息給前端</p><pre><code class="javascript hljs">notifyGetReady = <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
    <span class="hljs-keyword">var</span> s = (data.status == <span class="hljs-string">"ok"</span>) ? { <span class="hljs-attr">type</span>: <span class="hljs-string">"COMPUTING"</span>, <span class="hljs-attr">finish</span>: <span class="hljs-literal">true</span> } : { <span class="hljs-attr">type</span>: <span class="hljs-string">"COMPUTING"</span>, <span class="hljs-attr">finish</span>: <span class="hljs-literal">false</span> };
    sendJson(data.game.players[<span class="hljs-number">0</span>].ws, s);
    sendJson(data.game.players[<span class="hljs-number">1</span>].ws, s);
}
</code></pre><h4 id="題目">題目</h4><p>要取得題目，只需要呼叫 getQuestion([Gamd ID]) 即可，會回傳 que (題目內容) 或 null (沒有剩下的題目了)，傳送題目的程式如下，若沒有剩下題目，我們則傳送 <strong>END</strong> 訊息到前端。</p><pre><code class="javascript hljs">sendQuestion = <span class="hljs-function">(<span class="hljs-params">gameid</span>) =&gt;</span> {
    gm.getQuestion(gameid).then(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (data != <span class="hljs-literal">null</span>) {
            <span class="hljs-keyword">var</span> s = {
                <span class="hljs-attr">type</span>: <span class="hljs-string">"QUESTION"</span>,
                <span class="hljs-attr">time</span>: data.que.time,
                <span class="hljs-attr">que</span>: data.que.description,
                <span class="hljs-attr">ans</span>: data.que.ans,
                <span class="hljs-attr">id</span>: data.que.id
            };
            sendJson(data.d[<span class="hljs-number">0</span>], s);
            sendJson(data.d[<span class="hljs-number">1</span>], s);
            <span class="hljs-keyword">var</span> g = gm.getGameByID(gameid);
            setTimeout(sendResult, s.time * <span class="hljs-number">1000</span>, g, g.nowquestion);
        }
        <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">var</span> game = gm.getGameByID(gameid);
            <span class="hljs-keyword">var</span> re = { <span class="hljs-attr">type</span>: <span class="hljs-string">"END"</span>, <span class="hljs-attr">data</span>: gm.getEndResult(game) };
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">2</span>; i++) {
                re.id = i;
                sendJson(game.players[i].ws, re);
            }

        }

    });
}
</code></pre><p>我們在送完題目後馬上開啟計時器(setTimeout)，這是為了在使用者超過時間還沒有回傳答案時，公布結果。</p><h3 id="計分">計分</h3><h4 id="回答問題">回答問題</h4><p>回答問題時，使用 solveQuestion([WS],[Answer])<br>
程式會判斷是哪位使用者回答(相同Facebook ID可能會出錯)<br>
使用者回答後，我們隔 1 秒公布答案。</p><pre><code class="javascript hljs"> gm.solveQuestion(ws, msg.choose).then(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
	<span class="hljs-keyword">if</span> (data) {
		<span class="hljs-keyword">var</span> g = gm.getGameByID(ws.gameid);
        	setTimeout(sendResult, <span class="hljs-number">1000</span>, g, g.nowquestion);
	}
}).catch(<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> { });
</code></pre><h4 id="公布答案">公布答案</h4><p>getResult([Game]) 可取得答案及計分，我們在程式中判斷 <strong>nq</strong> 是否等於 nowquestion ，這樣可以避免回答到其他題目的答案。<br>
送出結果後，我們隔 2 秒，進行下一題。</p><pre><code class="javascript hljs">sendResult = <span class="hljs-function">(<span class="hljs-params">game, nq</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (game.nowquestion == nq) {
        <span class="hljs-keyword">var</span> re = { <span class="hljs-attr">type</span>: <span class="hljs-string">"RESULT"</span>, <span class="hljs-attr">data</span>: gm.getResult(game) };
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">2</span>; i++) {
            re.id = i;
            sendJson(game.players[i].ws, re);
        }
        setTimeout(sendQuestion, <span class="hljs-number">2000</span>, game.id);
    }
}
</code></pre><h2 id="總結">總結</h2><p><img src="https://i.imgur.com/MzHGB9B.png" alt=""></p><p>目前題目只會有兩種(猜生日和按讚)，讀者可以自行修改 question.js 中的程式來增加不同的題目！</p><p>您可以到 <a href="https://github.com/oscar60310/NodeJsClass4" target="_blank">Github</a> 上瀏覽完整的專案和教學</p><p>謝謝您把文章看完，我們的系列課程也到一段落了，希望你們會喜歡。有任何其他問題，歡迎聯絡我們。</p><p>第十一屆微軟學生大使　技術組　蔡臻平、詹鈞婷、盧俊言、鄭薇、王采楓、何天與　撰寫</p><h2 id="node-js-技術小聚課程">Node JS 技術小聚課程</h2><ol>
<li><a href="https://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/06/nodejs/" target="_blank">Node js 實作課程：文字雲</a></li>
<li><a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/05/09/nodejs-2" target="_blank">Node js 實作課程：回憶影片</a></li>
<li><a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/05/12/nodejs-2" target="_blank">Node js 實作課程：小遊戲製作</a></li>
</ol></div>]]></content:encoded>
    </item>
    <item>
      <title>【Node JS】Node js 實作課程：回憶影片</title>
      <link>https://docs.microsoft.com/archive/blogs/microsoft_student_partners_in_taiwan/nodejs-2</link>
      <pubDate>Wed, 07 Jun 2017 14:00:36 GMT</pubDate>
      <dc:creator><![CDATA[Microsoft Student Partners in Taiwan]]></dc:creator>
      <guid
        isPermaLink="false">http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?p=7335</guid>
      <description><![CDATA[Node js 回憶影片課程討論這次的專案使用到了 Facebook Graph API，還不熟悉的讀者，可以參考我們先前的文章，或是到 Facebook...]]></description>
      <content:encoded><![CDATA[<div id="doc" class="markdown-body container-fluid"><h1 id="node-js-回憶影片課程討論">Node js 回憶影片課程討論</h1><p>這次的專案使用到了 Facebook Graph API，還不熟悉的讀者，可以參考我們先前的<a href="https://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/06/nodejs/" target="_blank">文章</a>，或是到 <a href="https://developers.facebook.com/docs/graph-api" target="_blank">Facebook 開發人員中心</a>查看說明。<!--more-->

</p><h2 id="簡介"><strong>簡介</strong></h2><p>這次的課程我們會沿用上一次<a href="https://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/06/nodejs/" target="_blank">文字雲</a>課程的 Facebook Graph API，來繼續實作回憶錄的功能，可以讓大家選擇自己在 Facebook 的照片變成一部回憶錄的影片並在網頁顯示出來，那除了上次課程使用到的 <a href="https://code.visualstudio.com/" target="_blank">VS Code</a>，還需要另外安裝 <a href="https://nodejs.org/en/download/current/" target="_blank">NodeJs (7.6.0)</a> 以上的版本，原因會在後面程式碼為大家解說。</p><p>這次課程除了讓大家複習 Facebook Graph API 認證程序，也會帶大家使用套件來進行撰寫，主要在 NodeJS 的撰寫上，所以前端的部份我們不會解釋，如果有任何疑問，都可以在下方留言或進入社團跟我們交流。</p><p>這次完成的作品<br>
<img src="https://i.imgur.com/lGabHn4.jpg" alt=""></p><h2 id="使用-nodejs-實作授權及取得資料"><strong>使用 NodeJs 實作授權及取得資料</strong></h2><p>一樣，在開始之前，請前往<a href="https://github.com/alice0329/fbvedio-student" target="_blank">下載</a>課程檔案，裡面包還含了 npm 設定、VS code 設定、ffmpeg執行檔及前端資源。</p><h3 id="申請應用程式"><strong>申請應用程式</strong></h3><p>這個部分請參考上一篇 Node js 文字雲課程討論，步驟都是一樣的，不過因為這次範例我們是使用<strong>粉絲專頁存取權杖</strong>，用來取得使用者粉絲專業的資料，所以在<strong>導向授權頁面</strong>，我們傳回三個參數中的 scope，除了 user_posts 這個權限以外，我們還需要加入 <strong>manage_pages</strong>、<strong>user_photos</strong>，之後再選擇同意授權，就可以取得我們想要的資料了。</p><p>讓我們看一下程式碼修改之後的樣子吧。</p><pre><code class="javascript hljs">app.get(<span class="hljs-string">'/api/user'</span>, (req, res) =&gt; {
    <span class="hljs-keyword">var</span> re;
    <span class="hljs-keyword">if</span> (req.session.name)
        re = { <span class="hljs-attr">statu</span>: <span class="hljs-string">'ok'</span>, <span class="hljs-attr">name</span>: req.session.name };
    <span class="hljs-keyword">else</span>
        re = { <span class="hljs-attr">statu</span>: <span class="hljs-string">'not login'</span>, <span class="hljs-attr">url</span>: <span class="hljs-string">'https://www.facebook.com/v2.8/dialog/oauth?client_id='</span> + process.env.appID + <span class="hljs-string">'&amp;redirect_uri='</span> + process.env.redirect + <span class="hljs-string">'/api/code&amp;scope=user_posts&amp;scope=manage_pages&amp;scope=user_photos'</span> };
    res.setHeader(<span class="hljs-string">'Content-Type'</span>, <span class="hljs-string">'application/json'</span>);
    res.end(<span class="hljs-built_in">JSON</span>.stringify(re));
});
</code></pre><h2 id="使用者相簿資料"><strong>使用者相簿資料</strong></h2><p>我們先使用<a href="https://developers.facebook.com/tools/explorer/" target="_blank">測試工具</a>來測試一下結果。</p><p>請按下<strong>取得權杖 → 取得用戶存取權杖</strong>，勾選 user_posts、manage_pages、user_photos。<br>
<img src="https://i.imgur.com/MQkosSj.png" alt=""></p><p>接著試試看 <strong>me/albums</strong> 端點，可以看到有好幾個 id，一個 id 就是用戶的一個相簿，隨便點選一個 id，就是去搜尋那個相簿裡面的資料，然後再到左手邊的<strong>搜尋欄位</strong>輸入 photos 以及 images，按下<strong>提交</strong>。<br>
<img src="https://i.imgur.com/t1KKowT.png" alt=""></p><p>就可以看到相簿裡面每一張不同大小相片的網址。<br>
我們要取得相片的網址並將它下載至本機端。</p><p>在看 code 之前，我們先來介紹今天 code 會用到的一些套件，並於程式中require這些套件。</p><table>
<thead>
<tr>
<th>引用的套件</th>
<th>功用</th>
</tr>
</thead>
<tbody>
<tr>
<td>express</td>
<td>Express 是最小又靈活的 Node.js Web 應用程式架構，為 Web 與行動式應用程式提供一組健全的特性</td>
</tr>
<tr>
<td>express-session</td>
<td>將資料儲存在伺服器上</td>
</tr>
<tr>
<td>request</td>
<td>向 http 發出請求</td>
</tr>
<tr>
<td>fs</td>
<td>fs 是 filesystem 的縮寫，提供文件的讀寫能力</td>
</tr>
<tr>
<td>videoshow</td>
<td>簡單的實用工具，使用 ffmpeg 來製作圖像的視頻幻燈片，例如音頻，字幕和幻燈片之間的淡入/淡出轉換等附加功能</td>
</tr>
<tr>
<td>sharp</td>
<td>用來處理圖片</td>
</tr>
</tbody>
</table><p>說這麼多先來看看 code 怎麼打。</p><pre><code class="javascript hljs"><span class="hljs-comment">// GET 取得相簿資料</span>
app.get(<span class="hljs-string">'/api/albums'</span>, (req, res) =&gt; {
    getAlbums(req.session.key).then(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
        res.setHeader(<span class="hljs-string">'Content-Type'</span>, <span class="hljs-string">'application/json'</span>);
        res.end(<span class="hljs-built_in">JSON</span>.stringify(data));
    });
});
<span class="hljs-comment">// 讀相簿列表 </span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAlbums</span>(<span class="hljs-params">key</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
        <span class="hljs-keyword">var</span> qs = {
            <span class="hljs-attr">access_token</span>: key
        };
        request({
            <span class="hljs-attr">url</span>: <span class="hljs-string">'https://graph.facebook.com/v2.8/me/albums'</span>,
            qs
        }, (error, response, body) =&gt; {
            resolve(<span class="hljs-built_in">JSON</span>.parse(body));
        });
    });
}
</code></pre><p>將資料傳送到前端，就可以選擇其中一個相簿。</p><p>那我們就接著繼續完成 Code 吧</p><pre><code class="javascript hljs"><span class="hljs-comment">// GET 取得相片資料傳給 getImage</span>
app.get(<span class="hljs-string">'/api/create'</span>, (req, res) =&gt; {
    getPhotos(req.session.key, req.query.id).then(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
        getImage(data, res);
    });
});
<span class="hljs-comment">// 讀取特定相簿的相片列表</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPhotos</span>(<span class="hljs-params">key, id</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
        <span class="hljs-keyword">var</span> qs = {
            <span class="hljs-attr">fields</span>: <span class="hljs-string">'images'</span>,
            <span class="hljs-attr">access_token</span>: key
        };
        request({
            <span class="hljs-attr">url</span>: <span class="hljs-string">'https://graph.facebook.com/v2.8/'</span> + id + <span class="hljs-string">'/photos'</span>,
            qs
        }, (error, response, body) =&gt; {
            <span class="hljs-keyword">var</span> photo_data = <span class="hljs-built_in">JSON</span>.parse(body);
            <span class="hljs-keyword">var</span> photos = [];
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i <span class="hljs-keyword">in</span> photo_data.data) {
                photos.push(photo_data.data[i].images[<span class="hljs-number">0</span>].source);
            }
            resolve(photos);
        });
    });
}
</code></pre><p>做到這裡就可以取得相片的網址囉。</p><h2 id="合成影片"><strong>合成影片</strong></h2><h3 id="步驟">步驟</h3><p>取得相片列表後，我們使用前面介紹的 videoshow 模組把這些相片接成一個回憶影片。但是這個模組輸入的圖片一定要是相同大小，所以合成影片我們分成三個步驟：</p><p><img src="https://i.imgur.com/2y9RrVr.png" alt=""></p><p>在執行這三個步驟之前，我們要先清空存放下載圖片的 image 資料夾、存放調整過大小的 image_resized 資料夾，並確保兩個資料夾存在，以避免合成影片時發生錯誤。</p><p>接著我們就來寫 code 囉！首先先來看我們整個合成影片的步驟的 function。剩下的所有步驟都包含在這個 function 裡面了。但是我們每個步驟都需要執行完才能執行下一個步驟，結果才能完整呈現、不會出錯。所以我們要處理 javascript 的非同步問題。</p><p>記得上次課程用來處理非同步問題的 Promise 嗎? 這次我們介紹 async/await 的寫法搭配 Promise 使用，可以讓你的程式碼更好讀易懂！</p><pre><code class="javascript hljs"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getImage</span>(<span class="hljs-params">key, res</span>) </span>{
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">await</span> clearFolder(); <span class="hljs-comment">// 清空image資料夾和image_resized資料夾</span>
        <span class="hljs-keyword">await</span> urlImage(key); <span class="hljs-comment">// 下載照片</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; key.length; i++) {
            <span class="hljs-keyword">await</span> imageResize(<span class="hljs-string">"./image/"</span> + i + <span class="hljs-string">".jpg"</span>, <span class="hljs-string">"./image_resized/"</span> + i + <span class="hljs-string">".jpg"</span>, <span class="hljs-number">960</span>, <span class="hljs-number">720</span>); <span class="hljs-comment">// 重設圖片大小</span>
        }
        videoGen(res); <span class="hljs-comment">// 合成影片</span>
    } <span class="hljs-keyword">catch</span> (e) {
        <span class="hljs-built_in">console</span>.log(e);
    }
}
</code></pre><p>在function前面加"async"，function裡面就可以使用await。await可以接Promise物件。await要接到這個非同步的結果回傳後，才會執行下一行。</p><p>以下分別介紹每個步驟的function。</p><h2 id="1清空資料夾">1.清空資料夾</h2><pre><code class="javascript hljs"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">clearFolder</span>(<span class="hljs-params"></span>) </span>{  
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
        <span class="hljs-keyword">var</span> fileUrl = <span class="hljs-string">"./image"</span>;
        <span class="hljs-keyword">var</span> fileUrl2 = <span class="hljs-string">"./image_resized"</span>;
        <span class="hljs-keyword">if</span>(fs.existsSync(fileUrl)){
            <span class="hljs-keyword">var</span> files = fs.readdirSync(fileUrl);
            files.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">file</span>) </span>{
                fs.unlinkSync(fileUrl + <span class="hljs-string">'/'</span> + file);
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"删除文件"</span> + fileUrl + <span class="hljs-string">'/'</span> + file + <span class="hljs-string">"成功"</span>);
            });
        }
        <span class="hljs-keyword">else</span>{
            fs.mkdirSync(fileUrl);
        }     
        <span class="hljs-keyword">if</span>(fs.existsSync(fileUrl2)){
            <span class="hljs-keyword">var</span> files = fs.readdirSync(fileUrl2);
            files.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">file</span>) </span>{
                fs.unlinkSync(fileUrl2 + <span class="hljs-string">'/'</span> + file);
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"删除文件"</span> + fileUrl2 + <span class="hljs-string">'/'</span> + file + <span class="hljs-string">"成功"</span>);
            });
        }
        <span class="hljs-keyword">else</span>{
            fs.mkdirSync(fileUrl2);
        } 
        resolve();
    })
}
</code></pre><p>在刪除資料夾內的檔案之前，要先確認資料夾存在 ( existsSync() )，否則程式會出錯。如果檢查到資料夾不存在，就要建一個資料夾 ( mkdirSync() )，等等才有地方存放圖片。<br>
因為我們有兩個資料夾 (./image、./image_resized) 都要清空，所以大家會看到我們幾乎相同的code重複了兩次。</p><h2 id="2下載圖片">2.下載圖片</h2><pre><code class="javascript hljs"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">urlImage</span>(<span class="hljs-params">url</span>) </span>{   
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
        url.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">url_data, index, array</span>) </span>{
            <span class="hljs-keyword">let</span> stream = request(url_data).pipe(fs.createWriteStream(<span class="hljs-string">"./image/"</span> + index + <span class="hljs-string">".jpg"</span>))
            <span class="hljs-keyword">if</span> (index === array.length - <span class="hljs-number">1</span>)
                stream.on(<span class="hljs-string">'finish'</span>, () =&gt; resolve())
        })
    })
}
</code></pre><p>到這裏我們就成功把選擇的資料夾裡面的照片全部下載到 image 資料夾了。接下來我們要將圖片重設大小來配合 videoshow 套件的要求。</p><h2 id="3重設圖片大小">3.重設圖片大小</h2><pre><code class="javascript hljs"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">imageResize</span>(<span class="hljs-params">imgPath, imgPath_resized, width, height</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
        sharp(imgPath)
        .resize(width, height)
        .ignoreAspectRatio()
        .toFile(imgPath_resized, (err, info) =&gt;{
            resolve()
        });
    })
}
</code></pre><p>這裡我們用到 sharp 這個套件。如果你的 node_modules 資料夾裡面沒有 sharp這個資料夾，記得要先執行 npm install sharp 來安裝套件，並引入 sharp 模組。</p><pre><code class="javascript hljs"><span class="hljs-keyword">var</span> sharp = <span class="hljs-built_in">require</span>(<span class="hljs-string">'sharp'</span>);
</code></pre><h2 id="4使用-videoshow-合成影片">4.使用 videoshow 合成影片</h2><p>在使用 videoshow 之前，也要先安裝套件：</p><pre><code class="javascript hljs"><span class="hljs-keyword">var</span> videoshow = <span class="hljs-built_in">require</span>(<span class="hljs-string">'videoshow'</span>);
</code></pre><p>然後到 <a href="http://ffmpeg.org/download.html" target="_blank">FFmpeg 官網</a> 去下載 FFmpeg 的binary檔案 (ffmpeg.exe 和 ffprobe.exe)。<br>
並在程式中加入下面這兩行，並注意檔案路徑：</p><pre><code class="javascript hljs">videoshow.ffmpeg.setFfmpegPath(__dirname + <span class="hljs-string">'/ffmpeg/ffmpeg.exe'</span>)
videoshow.ffmpeg.setFfprobePath(__dirname + <span class="hljs-string">'/ffmpeg/ffprobe.exe'</span>)
</code></pre><p>我們在合成影片的時候需要設定影片和音樂的的一些參數：</p><pre><code class="javascript hljs"><span class="hljs-keyword">var</span> videoOptions = {
    <span class="hljs-attr">fps</span>: <span class="hljs-number">25</span>,
    <span class="hljs-attr">loop</span>: <span class="hljs-number">5</span>, <span class="hljs-comment">// seconds</span>
    transition: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">transitionDuration</span>: <span class="hljs-number">1</span>, <span class="hljs-comment">// seconds</span>
    videoBitrate: <span class="hljs-number">1024</span>,
    <span class="hljs-attr">videoCodec</span>: <span class="hljs-string">'libx264'</span>,
    <span class="hljs-attr">size</span>: <span class="hljs-string">'640x?'</span>,
    <span class="hljs-attr">audioBitrate</span>: <span class="hljs-string">'128k'</span>,
    <span class="hljs-attr">audioChannels</span>: <span class="hljs-number">2</span>,
    <span class="hljs-attr">format</span>: <span class="hljs-string">'mp4'</span>,
    <span class="hljs-attr">pixelFormat</span>: <span class="hljs-string">'yuv420p'</span>
}

<span class="hljs-keyword">var</span> audioParams = {
    <span class="hljs-attr">fade</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">delay</span>: <span class="hljs-number">2</span> <span class="hljs-comment">// seconds</span>
}
</code></pre><p>然後就是重要的影片合成啦！</p><pre><code class="javascript hljs"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">videoGen</span>(<span class="hljs-params">res</span>) </span>{
    <span class="hljs-keyword">var</span> images = [];
    fs.readdir(__dirname + <span class="hljs-string">'/image_resized'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, files</span>) </span>{
        <span class="hljs-keyword">if</span> (err) <span class="hljs-keyword">return</span>;
        files.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">f</span>) </span>{
            images.push(__dirname + <span class="hljs-string">'/image_resized/'</span> + f );
            <span class="hljs-comment">//console.log('Files: ' + f);</span>
            <span class="hljs-keyword">return</span> images;
        });
        <span class="hljs-built_in">console</span>.log(images);
        <span class="hljs-keyword">var</span> audio = __dirname + <span class="hljs-string">'/music/About_That_Oldie.mp3'</span>
        videoshow(images, videoOptions)
            .audio(audio, audioParams)
            .save(<span class="hljs-string">'./static/output/video.mp4'</span>)
            .on(<span class="hljs-string">'start'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">command</span>) </span>{
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'ffmpeg process started:'</span>, command)
            })
            .on(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err</span>) </span>{
                <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, err)
            })
            .on(<span class="hljs-string">'end'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">output</span>) </span>{
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Video created in:'</span>, output)
                <span class="hljs-keyword">var</span> re;
                re = {
                    <span class="hljs-attr">statu</span>: <span class="hljs-string">'ok'</span>,
                };
                res.setHeader(<span class="hljs-string">'Content-Type'</span>, <span class="hljs-string">'application/json'</span>);
                res.end(<span class="hljs-built_in">JSON</span>.stringify(re));
            })
    });
}
</code></pre><p>我們先去 image_resized 把照片一張一張讀出來 ( readdir() )，然後塞進 image 陣列中。再指定音樂的路徑後，就可以用 videoshow 把影片做出來了。在影片合成完之後，就傳送成功的訊息給前端，在前端顯示合成完的影片。</p><p>在執行之前，不要忘記把前面取得相簿資料那步驟的傳送資料動作註解掉喔！</p><pre><code class="javascript hljs">app.get(<span class="hljs-string">'/api/albums'</span>, (req, res) =&gt; {
    getAlbums(req.session.key).then(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
        <span class="hljs-comment">//res.setHeader('Content-Type', 'application/json');</span>
        <span class="hljs-comment">//res.end(JSON.stringify(data));</span>
    });
});
</code></pre><p>這樣就完成我們整個回憶錄的製作了！</p><h1 id="總結">總結</h1><p>看過這篇文章後，你應該對 Facebook Graph API 的影片應用有更進一步的瞭解了，可以前往 <a href="https://github.com/alice0329/fbvideo" target="_blank">範例 Github</a> 看看完整程式，我們還有其他課程，若有興趣，可以加入<a href="https://www.facebook.com/groups/126283061232907/" target="_blank">課程社團</a> 一起討論！</p><p>第十一屆微軟學生大使　技術組　王采楓、何天與　撰寫</p><h2 id="node-js-技術小聚課程">Node JS 技術小聚課程</h2><ol>
<li><a href="https://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/06/nodejs/" target="_blank">Node js 實作課程：文字雲</a></li>
<li><a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/05/09/nodejs-2" target="_blank">Node js 實作課程：回憶影片</a></li>
<li><a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/05/12/nodejs-2" target="_blank">Node js 實作課程：小遊戲製作</a></li>
</ol></div>]]></content:encoded>
    </item>
    <item>
      <title>【Power BI】實作系列文1_介紹+安裝註冊</title>
      <link>https://docs.microsoft.com/archive/blogs/microsoft_student_partners_in_taiwan/powerbi_handon_1</link>
      <pubDate>Sun, 09 Apr 2017 00:30:06 GMT</pubDate>
      <dc:creator><![CDATA[Microsoft Student Partners in Taiwan]]></dc:creator>
      <guid
        isPermaLink="false">http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?p=5155</guid>
      <description><![CDATA[其他系列文
1. Power BI介紹+安裝註冊
2. 匯入資料與資料轉型
3. 資料模型介紹
4. 坐標軸+視覺化
5. 地圖+卡片+多列卡片
6. KPI...]]></description>
      <content:encoded><![CDATA[<h3 style="text-align: center"><strong><u><img src="https://msdnshared.blob.core.windows.net/media/2017/03/powerBI.jpg" alt="powerbi" width="1270" height="415" class="size-full wp-image-5215 alignnone" />
其他系列文</u></strong></h3>
<p style="text-align: center"><a href="https://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/09/powerbi_handon_1/">1. Power BI介紹+安裝註冊</a></p>
<p style="text-align: center"><a href="https://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/09/powerbi_handon_2/">2. 匯入資料與資料轉型</a></p>
<p style="text-align: center"><a href="https://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/09/powerbi_handon_3/">3. 資料模型介紹</a></p>
<p style="text-align: center"><a href="https://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/09/powerbi_handon_4/">4. 坐標軸+視覺化</a></p>
<p style="text-align: center"><a href="https://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/09/powerbi_handon_5/">5. 地圖+卡片+多列卡片</a></p>
<p style="text-align: center"><a href="https://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/09/powerbi_handon_6/">6. KPI +交叉分析篩選器</a></p>
&nbsp;
<h2><strong><u>前言</u></strong></h2>
<strong>        </strong>相信大家以前在處理一些數據<span>, </span>做時間表之類的一定都有用過 <span>Microsoft Excel </span>。<span>Excel </span>主力用在科學數值計算上，使用一些公式去整理資料等等。雖然 <span>Excel </span>同樣也有圖表功能達到一定程度的可視化的功能，卻在資料可視化，即使的資訊反映則無法完美做到。

Power BI 是一套資料可視化的服務。整個服務包括了  <span>Power BI Desktop</span>，<span>Power BI Service</span>，<span>Power BI Mobile </span>。用家可以在 <span>Power BI Desktop </span>上整理資料，設計好要展示的 <span>Dashboard </span>以及資料即時的關係反映，然後發佈到  <span>Power BI Service</span>。這樣不論你在哪，都可以即使連接上 <span>Power BI Service </span>進行資訊的展示。

<span>讀完此篇文章後，您將學會：</span>
<span><a href="#1_1">．安裝</a></span><span>
</span><span><a href="#1_2">．註冊</a></span><span>
</span><span><a href="#1_3">．教材下載</a></span>

<!--more-->

<img src="https://msdnshared.blob.core.windows.net/media/2017/03/powerBI_11.png" alt="powerbi_11" width="1200" height="600" class="size-full wp-image-5245 alignnone" />

圖片出處： <span><a href="https://powerbi.microsoft.com/zh-tw/guided-learning/powerbi-learning-0-0-what-is-power-bi/">Power BI 官網</a></span>

<strong>Power BI </strong><strong>執行的所有分析通常都是遵循一般流程。<span> Power BI </span>中活動的一般流程如下：</strong>
<ul>
 	<li>將資料帶入<span> Power BI Desktop </span>並建立報表。</li>
 	<li>發行至<span> Power BI </span>服務，您可以在其中建立新的視覺效果，或建置儀表板</li>
 	<li>與其他人共用儀表板，尤其是行動工作者</li>
 	<li>使用 <span>Power BI Mobile </span>應用程式來檢視共用的儀表板和報表，並與之互動</li>
</ul>
&nbsp;
<h2><strong><u></u></strong></h2>
<h2><strong><u>安裝
</u></strong></h2>
首先，我們需要下載所有必要工具。

【PowerBI工作坊】

PowerBI工作坊的教材所使用的版本為前一版本，由於教學上的需求，

<strong><u>請工作坊參加者下載我們提供的載點喔！！！</u></strong>

教學版本載點：<a href="https://1drv.ms/u/s!Ajf4brKkzbRp6Fe4Eo5NV-_YthEJ"><span>工作坊</span> PowerBI Desktop <span>下載</span></a>

點選下載
<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/09/powerbi_handon_1/new1_1/"><img src="https://msdnshared.blob.core.windows.net/media/2017/04/new1_1.png" alt="new1_1" width="1366" height="768" class="size-full wp-image-7305 alignnone" /></a>
<p style="background: white;margin: 0cm 0cm 7.5pt 0cm"><span style="font-family: '微軟正黑體',sans-serif">之後出現此畫面，仍然點選下載<span lang="EN-US"></span></span></p>
<p style="background: white;margin: 0cm 0cm 7.5pt 0cm"><a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/09/powerbi_handon_1/new1_2/"><img src="https://msdnshared.blob.core.windows.net/media/2017/04/new1_2.png" alt="new1_2" width="1366" height="768" class="size-full wp-image-7315 alignnone" /></a></p>
<p style="background: white;margin: 0cm 0cm 7.5pt 0cm"><span style="font-family: '微軟正黑體',sans-serif">下載後到路徑解壓縮就會出現<span lang="EN-US"></span></span></p>
<img src="https://msdnshared.blob.core.windows.net/media/2017/03/powerBI_14.png" alt="powerbi_14" width="496" height="386" class="size-full wp-image-5265 alignnone" />

只要一直按<span>Next</span>，把<span>Power BI Desktop </span>安裝好囉。
<a></a>
<h2><strong><u>註冊</u></strong></h2>
PowerBI帳號註冊：<span><a href="https://powerbi.microsoft.com/zh-tw/get-started/">官方網站網址</a></span>
<ol>
 	<li>接著回來網站點選右邊的<span>”</span>註冊<span><span>”
<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=6316"><img src="https://msdnshared.blob.core.windows.net/media/2017/03/powerBI_01002.png" alt="powerbi_01002" width="1341" height="636" class="size-full wp-image-6316 alignnone" /></a></span></span></li>
 	<li>輸入自己學校的信箱
<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/?attachment_id=6305"><img src="https://msdnshared.blob.core.windows.net/media/2017/03/powerBI_01003.png" alt="powerbi_01003" width="1364" height="553" class="size-full wp-image-6305 alignnone" /></a></li>
 	<li>到學校信箱收信然後就完成開通囉</li>
</ol>
<a></a>
<h2><strong><u>教材下載</u></strong></h2>
另外，為了方便學習，讓我們先把資料檔下載下來本機端。

資料檔下載網址：<span><a href="https://1drv.ms/u/s!Ajf4brKkzbRp6FZtx0x_ft6OncvH">資料集下載網址</a></span>

點選下載網址後出現此畫面，接著按下載

<a href="http://blogs.msdn.microsoft.com/microsoft_student_partners_in_taiwan/2017/04/09/powerbi_handon_1/new1_3/"><img src="https://msdnshared.blob.core.windows.net/media/2017/04/new1_3.png" alt="new1_3" width="1366" height="768" class="size-full wp-image-7285 alignnone" /></a>

解壓縮後可以看到以下畫面

<img src="https://msdnshared.blob.core.windows.net/media/2017/03/powerBI_15.png" alt="powerbi_15" width="690" height="287" class="size-full wp-image-5225 alignnone" />

點選<span>INTERNATION</span>資料夾後可以看到以下檔案

<img src="https://msdnshared.blob.core.windows.net/media/2017/03/powerBI_16.png" alt="powerbi_16" width="677" height="206" class="size-full wp-image-5235 alignnone" />

接下來，你只要像我一樣將資料取得後，就可以自己試試看將這些資料視覺化呈現囉！或者繼續看下一篇部落文就會教如何匯出資料還有將資料轉型喔！

<img src="https://msdnshared.blob.core.windows.net/media/2017/03/powerBI_17.png" alt="powerbi_17" width="1268" height="560" class="size-full wp-image-5275 alignnone" />

&nbsp;
<h3><strong>出處及參考文件</strong><strong>：</strong></h3>
<ol>
 	<li>Power BI 官網：<span><span><span><a href="https://powerbi.microsoft.com/zh-tw/">https://powerbi.microsoft.com/zh-tw/</a></span></span></span></li>
</ol>
&nbsp;

｜撰寫者：第十一屆 MSP 技術組　楊方寧
<img src="https://msdnshared.blob.core.windows.net/media/2017/03/Becky-Yang-1024x259.jpg" alt="becky-yang" width="512" height="130" class="alignnone wp-image-5295" />]]></content:encoded>
    </item>
  </channel>
</rss>