2013年10月31日 星期四

老生常談--MVC action result 如何輸出檔案?

基本上asp.net web form 方式的檔案下載大家應該很熟了
======例,在某列中找出下載的檔案資料===============
 protected void DownloadDoc_Click(object sender, EventArgs e) {
            ImageButton imageButton = (ImageButton)sender;
            TableCell tableCell = (TableCell)imageButton.Parent;
            GridViewRow row = (GridViewRow)tableCell.Parent;
            ;
            //GridView1.SelectedIndex = row.RowIndex;
            //doc path is found by the datakey attribute of grid and corresponding field/column
            string docPath = GridView1.DataKeys[row.RowIndex]["DOC_PATH"].ToString();
            //trigger the download process.
            string filename = Path.GetFileName(docPath);
            System.IO.Stream stream = null;
            try
            {
                // Open the file into a stream.
                stream = new FileStream(docPath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read);
                // Total bytes to read:
                long bytesToRead = stream.Length;
                Response.ContentType = "application/octet-stream";
                string fileExt = Path.GetExtension(docPath);
                Response.AddHeader("Content-Disposition", "attachment; filename=" +
                    util.MisFunc.ConvertDateTimeToJavaMilliSecond(System.DateTime.Now) + fileExt);
                   
                // Read the bytes from the stream in small portions.
                while (bytesToRead > 0)
                {
                    // Make sure the client is still connected.
                    if (Response.IsClientConnected)
                    {
                        // Read the data into the buffer and write into the
                        // output stream.
                        byte[] buffer = new Byte[10000];
                        int length = stream.Read(buffer, 0, 10000);
                        Response.OutputStream.Write(buffer, 0, length);
                        Response.Flush();
                        // We have already read some bytes.. need to read
                        // only the remaining.
                        bytesToRead = bytesToRead - length;
                    }
                    else
                    {
                        // Get out of the loop, if user is not connected anymore..
                        bytesToRead = -1;
                    }
                }
            }
            catch (Exception ex)
            {
                Response.Write(ex.Message);
                // An error occurred..
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }
            }
        }
====================================================
那ASP.net MVC4呢?
==============MVC4的寫法更簡潔======================
using System.Net.Mime;
using System.IO;
...
...
 public FileStreamResult getPdfFile(string path) {
                        string path = (string)recFile["DOC_PATH"];
                        var contentDisposition = new ContentDisposition
                        {
                            FileName = "doc.pdf",
                            Inline = true
                        };
                        Response.AppendHeader("Content-Disposition", contentDisposition.ToString());
                        byte[] bytes = System.IO.File.ReadAllBytes(path); //ReportStore.GetProfileInByte(path);
                        MemoryStream ms = new MemoryStream(bytes);
                        return new FileStreamResult(ms, MediaTypeNames.Application.Pdf);
 
}
================================================

2013年10月27日 星期日

for sencha-touch store 載入一堆空白的資料列!! 那會安爾!?

今天在實作下拉載入分頁的List時
(好吧,先分享一下最短的實作好了,也不過是從書上抄來的)
====sample as below=======================
     Ext.Viewport.setMasked({xtype:'loadmask',message:'Loading Data....'});
      var storeFiles=Ext.create('Ext.data.Store', {
                autoDestroy:true,
                model: 'TocMobile.model.FileMdl',
                pageSize:4,
                //storeId: 'Store4Files',
                autoLoad:false,
                proxy: {
                    type: 'ajax',
                    actionMethods: {
                        create: 'POST',
                        read: 'POST',
                        update: 'POST',
                        destroy: 'POST'
                    },
                    extraParams: {
                        icNo: icNo,
                        password: password
                    },
                    url: 'http://'+SERVER_IP+'/TocMobile/.....',
                    reader: {
                        type: 'json',
                        rootProperty: 'rows'
                    }
                }
           
        });  
   
    storeFiles.load(
        function(){
               Ext.Viewport.setMasked(false);
                var fileTemplate=new Ext.XTemplate(
                    '<tpl for=".">',
                    '<div class="File2Download" >',
                    '  <span class="icondownlaod" >',
                    '      <img src="./Adobe_PDF_Icon.svg.png" width="40" height="40" />',
                    '  </span>',
                    '    <span class="UploadDate">{uploadDate}</span>',
                    '  <br>',
                    '      <span class="UploadDetail">{uploadRemark}</span>',
                    '</div>',                  
                    '</tpl>'
                );
                var list=Ext.create('Ext.List',{
                    store:storeFiles,
                    height:'100%',
                    itemTpl:fileTemplate,
                    plugins:[
                        {
                            xclass:'Ext.plugin.ListPaging',
                            autoPaging:true,
                            loadMoreText:'Next...'
                        }
                    ],
                    emptyText:'No Data!!'
                 
                });
                    var pnlFileList= Ext.getCmp('PnlMain4FileListUnit');
                    pnlFileList.getComponent('lblFileListStatus').setHtml("  File Listing");
                    pnlFileList.getComponent('pnlListFiles').removeAll();
                    pnlFileList.getComponent('pnlListFiles').add(list);
           
        }
    );
============end of sample code============================
在測試時,卻一直發生load時來了5百多筆的空白資料!!!
明明別人的程式都正常的啊
John組長眉頭一皺,案情並不單純是client的問題
原來.....是出在server side沒事把資料列用「new JavaScriptSerializer().Serialize(...)」
變成「json內的字串格式」
==============以下是錯誤的,小心!!=================
                        List<File2Download> listfile=new List<File2Download>();
                        ...
                       ...
                        var result = new
                        {
                            success = true,
                            total=toalRowCount,
                            rows =new JavaScriptSerializer().Serialize(listfile)
                        };
                      JsonResult jResult = new JsonResult();
                      jResult.Data = result;
                      jResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
            return jResult;
  
==============================================
吐出來的字串就成了
   "{"success":true,"total":30,"rows":[{\"uploadDate\":\"2013-08-29\",\"docPath\":\"test path\",\"uploadRemark\":\"test remark\"},...]}";

  哎,既然是用「JsonResult」,就給他放心去轉換成字串就是了嘛
  =======以下才是正確的==========================
                        List<File2Download> listfile=new List<File2Download>();
                        ...
                       ...
                        var result = new
                        {
                            success = true,
                            total=toalRowCount,
                            rows =listfile  //--->就這樣,別手賤!!
                        };
                      JsonResult jResult = new JsonResult();
                      jResult.Data = result;
                      jResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
            return jResult;
=========================================
吐出來的就成了:
  {"success":true,"total":30,"rows":[{"uploadDate":"2012-10-15","docPath":"test path","uploadRemark":"test remark"},...]}
 神奇的是Sencha Touch 的store物件會解開那堆字變成文字的成為一堆空白資料列而且還不會給你出現exception/error才叫麻煩吧
Anyway,總之,別隨便用「JavaScriptSerializer().Serialize(...)」方法就是了


2013年10月20日 星期日

Android包html5網頁,用到定位權限時的設定

最近先用動態網頁寫好再用android / ios去包成app的開發方式頗為流行
所以順勢把之前的掌上商城andriod版一點一點地改成mobile web來包看看
不過光是在定位時發生問題.
WebView要如何使用Goelocation的權限呢?
步驟1,在 AndroidManifest.xml 指定以下權限:
   ======================================
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_GPS" />
<uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS" />
<uses-permission android:name="android.permission.ACCESS_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />  
=============================================
步驟二,在WebView的setting中設定以下權限:
   (假設Activity中有個「mWebView.」WebView型態的變數)
    =============================================
        mWebView.getSettings().setGeolocationEnabled(true);
        mWebView.getSettings().setAppCacheEnabled(true);
        mWebView.getSettings().setDatabaseEnabled(true);
        mWebView.getSettings().setDomStorageEnabled(true);        
   =============================================
步驟三,在遇到請求權限時,包裝的app如何應對?
   ===========================================
          mWebView.setWebChromeClient(new WebChromeClient() {
        public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
           callback.invoke(origin, true, false);
        }
        });        
=====================================
以上,其他的就照大家最常看到的webview應用來做了


2013年10月18日 星期五

好文轉貼使用ASP.NET SimpleMembership 提供者

最近有遇到asp.net MVC4.5的 權限整合問題:
 原來的前後台版本是用WebMatrix/WebSecurity,
 而如今前台要改版成為行動版,而VS2012的mobile的範本的membership機制是用「Universal Provider」,這下子麻煩大了
所幸,前人已經踢過鐵板了,參考這篇連結吧
使用ASP.NET SimpleMembership 提供者
以及
To call this method, the “Membership.Provider” property must be an instance of “ExtendedMembershipProvider”

另外,以下摘錄實作程序(真的是我實過也試過可以run)
步驟一,修改web.config,加入原本auth的db connection
   =====================
  <connectionStrings>
    <add name="memberConnection" ........ providerName="System.Data.SqlClient" />
  </connectionStrings>
   =====================

步驟二.在web.config中,把原本的
==========================
   <profile defaultProvider="DefaultProfileProvider">
    ...
    ...
  </roleManager>
==============================
 改成以下的設定:
  ==============================================
 <profile defaultProvider="SimpleProfileProvider">
      <providers>
        <add name="SimpleProfileProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" connectionStringName="DefaultConnection" applicationName="/" />
      </providers>
    </profile>
    <membership defaultProvider="SimpleMembershipProvider">
      <providers>
        <add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
      </providers>
    </membership>
    <roleManager defaultProvider="SimpleRoleProvider">
      <providers>
        <add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData" />
      </providers>
    </roleManager>    
  ==============================================

步驟三,,安裝套件「WebMatrix.WebData」

步驟四,到Gloabal.aspx.cs的 Application_Start函數,加上webSecurity的起始設定
 (當然這是基於你已經把role/user_profile...等其他資料表都開好的假設)
  ==========================
 protected void Application_Start()
        {
          //memberConnection是我們在web.config中設定的connection string.
            WebSecurity.InitializeDatabaseConnection("memberConnection", "StoreManager", "UserId", "UserName", autoCreateTables: true);

            AreaRegistration.RegisterAllAreas();
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
  ==========================
步驟五,修改model中,RegisterModel等其他model的欄位,有的非必要的就把他的[Required]拿掉
步驟六.修改Account Controller--基本上都不必改,唯一要改的是「新建用戶帳號」那段
  因為實作是委託給WebSecurity,所以小改一下才會成功:
  =======原本========================
  public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // 嘗試註冊使用者
                MembershipCreateStatus createStatus;
                Membership.CreateUser(model.UserName, model.Password, model.Email, passwordQuestion: null, passwordAnswer: null, isApproved: true, providerUserKey: null, status: out createStatus);

                if (createStatus == MembershipCreateStatus.Success)
                {
                    FormsAuthentication.SetAuthCookie(model.UserName, createPersistentCookie: false);
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    ModelState.AddModelError("", ErrorCodeToString(createStatus));
                }
               
            }

            // 如果執行到這裡,發生某項失敗,則重新顯示表單
            return View(model);
        }
  ==============改為==================================
public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                try {
                    WebMatrix.WebData.WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
                    //自己再加入user & Role mapping table--「webpages_UsersInRoles」的inser動作吧...
                    FormsAuthentication.SetAuthCookie(model.UserName, createPersistentCookie: false);
                    return RedirectToAction("Index", "Home");
                }catch(Exception exp)
                {
                    ModelState.AddModelError("", exp.Message);
                }
            }

            // 如果執行到這裡,發生某項失敗,則重新顯示表單
            return View(model);
        }
======================================================
其他登入/改變密碼都沒問題.這樣算是大功告成了!!

以上,誌於 2013.10.19

2013年10月11日 星期五

for android:最小的取得經緯度函數

import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;

import com.neotech.goingtvmall.frontend.R.id;
import com.neotech.goingtvmall.frontend.SubStoreTypeListActivity.MyAdapter;

import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.ListView;
import android.widget.TextView;

public class GeolocationFunc {


public static String getAddress(Location location,Context ctxOfActivity)throws Exception{
        Geocoder geocoder;
        geocoder = new Geocoder(ctxOfActivity, Locale.getDefault());
List<Address> addresses=
geocoder.getFromLocation(location.getLatitude(),
                location.getLongitude(), 1);
//geocoder.getFromLocation(currentlocation.getLatitude(),currentlocation.getLongitude(), 1);
StringBuffer sb=new StringBuffer();
for(Address address : addresses){
   String addressLine = address.getAddressLine(0);
       String city = address.getAddressLine(1);
       String country = address.getAddressLine(2);
       Log.v("addr",addressLine+" "+city+" "+country);
       if(city==null)city="";
       if(country==null)country="";
       sb.append(addressLine+" "+city+" "+country+"附近");
}
return sb.toString();
}

public static Location getBestLocation(Context ctxt) {
   Location gpslocation = getLocationByProvider(
       LocationManager.GPS_PROVIDER, ctxt);
   Location networkLocation = getLocationByProvider(
       LocationManager.NETWORK_PROVIDER, ctxt);
   Location fetchedlocation = null;
   // if we have only one location available, the choice is easy
   if (gpslocation != null) {
       Log.i("New Location Receiver", "GPS Location available.");
       fetchedlocation = gpslocation;
   } else {
       Log.i("New Location Receiver",
           "No GPS Location available. Fetching Network location lat="
               + networkLocation.getLatitude() + " lon ="
               + networkLocation.getLongitude());
       fetchedlocation = networkLocation;
   }
   return fetchedlocation;
}

/**
* get the last known location from a specific provider (network/gps)
*/
private static Location getLocationByProvider(String provider, Context ctxt) {
   Location location = null;
   // if (!isProviderSupported(provider)) {
   // return null;
   // }
   LocationManager locationManager = (LocationManager) ctxt
           .getSystemService(Context.LOCATION_SERVICE);
   try {
       if (locationManager.isProviderEnabled(provider)) {
           location = locationManager.getLastKnownLocation(provider);
       }
   } catch (IllegalArgumentException e) {
       Log.i("New Location Receiver", "Cannot access Provider " + provider);
   }
   return location;
}
}

2013年10月2日 星期三

Windows Server 2012 評估版與延長使用期限

延長使用期限

跟 Windows Server 2008 延長使用天數的方法一樣,都可以重設授權 5 次,差別在於,執行重設授權並重新開機之後,Windows 就自動啟用了,不會先有 10 天的寬限期。
我們先用這個指令查看剩下的天數,以及可以重設授權 (Rearm) 的次數:
slmgr.vbs -dlv
在 180 天的使用天數即將到期前一、兩天,你需要再執行:
slmgr.vbs -rearm
重新開機後 Windows 又會有 180 天的試用期了。而重設授權的次數會少一次。經由重覆執行重設授權的步驟,理論上 Windows Server 2012 可以讓你合法試用 1080 天。
須注意的是,一定要在到期日前去重設授權,否則你會無法登入 Windows,只能重新安裝或輸入自己購買的 Windows 序號來啟用。而你如果有去更改系統時間,也有可能會造成這 180 天的試用期失效,如果發現試用期失效的話,也必須立即執行重設授權才行。

繼續延長使用期限

如果 1080 天的試用期到了,而你的機器還沒壞、公司還沒倒、你還在當 MIS,而你還是不想花錢要繼續 "試用" Windows Server 2012 的話,跟 Windows Server 2008 一樣,透過修改機碼來增加重設授權次數的技巧依然有用。
執行 "regedit",進到:
"HKEY_LOCAL_MACHINE" -> "SOFTWARE" -> "Microsoft" -> "Windows NT" -> "CurrentVersion" -> "Software" -> "ProtectionPlatform"

將 "SkipRearm" 的鍵值修改為 "1",改完又可以再用 "slmgr.vbs -rearm" 一次,據說此步驟可以用 8 次。


詳細內容:
http://www.vixual.net/blog/archives/180

2013年10月1日 星期二

win8 (64)之下,怎麼修改MAC Address

為什麼要修改MAC address?
有時某些遠端遙控軟體過了期用試之後,就可以用這方式重新得到一組新的id

在win8 (server 2012) 64中,如何修改?

請去 http://www.gorlani.com/portal/projects/macmakeup-for-vista-seven-2008-windows-8
下載「mcmkup.ps1」
用powershell 去執行
事實些就是一個指令「s 0 20Fe3092yz」
其中的0,指的是第0張網卡,
後面的20Fe3092xyz就是MAC address型態20:Fe:30:92:yz之意
當然你以用 「c」指令還原到初始值再指定其他Mac address
指令成功時會出現
Here is the list of  physical NICs on this host
0) Ethernet,[網卡型態],spoofed : xxxxxx <---你指定的Mac Address.
...
...

這樣就成功了
如此,重新啟動那個遠端遙控軟後,就會有新的id了
試看看吧