2012年10月1日 星期一

利用DotNetZip打包壓縮

最近系統需要提供使用者將多個檔案打包壓縮下載,經由google查詢後找出可利用DotNetZip完成此需求,以下為使用心得

首先我們先下載DotNetZip,解壓縮後將Ionic.Zip.dll放置到Web應用程式的Bin目錄下


接著在cs檔上方using
using System.IO;
using System.Text;
using Ionic.Zip;

由於多人同時打包壓縮下載可能會發生檔名衝突的問題,為了解決這個問題我們為個別使用者建立以使用者帳號為名的目錄,做為未來存放壓縮檔的位置,這裡以使用者Ken為例。
if (!Directory.Exists(@"D:\upload\tmp\Ken"))
{
    Directory.CreateDirectory(@"D:\upload\tmp\Ken");
}

因為每次挑選的檔案不同,為了簡化壓縮檔的命名規則,統一命名為MyZip.zip,在檔案下載前清空使用者目錄下的壓縮檔
DirectoryInfo directory = new DirectoryInfo(@"D:\upload\tmp\Ken");
foreach (FileInfo file in directory.GetFiles()) 
{
    file.Delete();
}

接著輸入未來要存放壓縮檔的位置及檔名,並輸入要加入壓縮檔的檔案,若檔案不存在則會出現System.IO.FileNotFoundException,所以必須檢查檔案是否存在,這裡的例子以a.txt、b.txt做為要加入壓縮檔的檔案。
ZipFile zip = new ZipFile(@"D:\upload\tmp\Ken\MyZip.zip", Encoding.UTF8);
if (File.Exists(@"D:\upload\a.txt"))
{
    zip.AddFile(@"D:\upload\a.txt", "");
}
if (File.Exists(@"D:\upload\b.txt"))
{
    zip.AddFile(@"D:\upload\b.txt", "");
}
zip.Save();

最後輸入下載的檔名及壓縮檔的位置及名稱即可。
Response.AddHeader("Content-Disposition", "attachment;filename=MyZip.zip");
Response.ContentType = "application/octet-stream";
Response.TransmitFile(@"D:\upload\tmp\Ken\MyZip.zip");

2012年2月12日 星期日

RDLC (三) 第一支報表

這篇我們將介紹如何撰寫第一支RDLC報表,首先我們先來看一下整個Web站台的目錄結構
以下說明各目錄或檔案的功用
  • 在App_Code底下我們建立一個Report的目錄,用來儲放報表檔使用的資料集物件。
  • 根目錄底下的Report目錄則儲放報表檔(Customers.rdlc)。
  • first.aspx嵌入ReportViewer控制項。
  • first.aspx.cs處理ReportViewer該讀取那個報表檔及資料繫結部份。
接著我們正式開始撰寫報表,首先我們先建立資料集物件,建立資料集物件的目的在於作為報表檔的Report Data,在App_Code目錄下的Report目錄上點選滑鼠右鍵新增項目
選擇資料集並將檔名命名為Customers.xsd後按下新增按鈕,
接著我們將Toolbox裡的DataTable拖曳至設計畫面並增加相關欄位
在完成資料集後我們進行報表設計,在根目錄下的Report目錄上按滑鼠右鍵新增報表,並將報表檔名命名為Customers.rdlc
進入報表設計畫面後我們先將事先定義好的資料集加入Report Data,如果沒有看到Report Data畫面,則需要利用滑鼠點擊報表設計畫面,然後同時按下Ctrl+Alt+D鍵
在Data Source下拉選單挑選Customers及在Available datasets下拉選單挑選Customer並將Name命名為Customer
加入資料集後將頁籤切換至Toolbox,將Table控制項拖曳到報表畫面後新增一個欄位
再依序將Report Data中的Customer資料集欄位拖曳至設計畫面裡Table的Data區塊
完成報表設計後我們進行網頁程式的撰寫,以下為aspx、aspx.cs內容及執行畫面
aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="first.aspx.cs" Inherits="first" %>
<%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
        <rsweb:ReportViewer ID="rptviewer" runat="server" Width="100%" Height="600px" />        
    </div>
    </form>
</body>
</html>
aspx.cs
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class first : System.Web.UI.Page
{
    protected void Page_Init(object sender, EventArgs e)
    {
        DataTable dt = new DataTable();
        SqlCommand cmd = new SqlCommand();
        cmd.Connection = new SqlConnection("Data Source=(local);Initial Catalog=Northwind;Integrated Security=SSPI;");
        cmd.CommandText = "SELECT CustomerID, CompanyName, ContactName, ContactTitle FROM Customers";
        SqlDataAdapter da = new SqlDataAdapter(cmd);

        try
        {
            da.Fill(dt);
        }
        catch (Exception ex)
        {
            throw ex;
        }
        finally
        {
            cmd.Connection.Close();
        }

        rptviewer.LocalReport.ReportPath = Server.MapPath("/WebSite/Report/Customers.rdlc");
        rptviewer.LocalReport.DataSources.Clear();
        rptviewer.LocalReport.DataSources.Add(new Microsoft.Reporting.WebForms.ReportDataSource("Customer", dt));
        rptviewer.LocalReport.Refresh();
    }
}


程式說明
  • LocalReport.ReportPath是指定報表所在路徑。
  • ReportDataSource("Customer", dt))是將dt(實際產出的資料)與資料集對應。
  • LocalReport.Refresh()使報表將資料render。

針對程式部份有幾處需要注意的
  1. aspx程式需要包含ScriptManager控制項,否則會產生錯誤。
  2. aspx.cs程式需將報表相關程式放在Page_Init事件,否則執行時畫面會一直refresh。
  3. dt的欄位名稱與資料集中的欄位名稱不同時,需要特別處理。



2012年1月31日 星期二

RDLC (二) 部署

在部署RDLC報表到Web站台後,為能確保能夠正常執行,我們必須利用下列任一方法建置執行環境
  1. 在Web Server上安裝ReportViewer.exe
  2. 將ReportViewer相關dll檔複製到ASP.NET應用程式的Bin目錄下。
方法1開發人員須具備安裝軟體的權限,在分工較細的公司主機是由Server管理員控管,一般而言Server管理員為了主機運行正常,通常不希望主機裝載額外的應用程式,所以在這裡我們只討論方法2,首先我們要在開發環境的PC,利用"命令提示字元"將目錄切換至C:\WINDOWS\assembly\GAC_MSIL>

並執行dir Microsoft.ReportViewer.*,由結果可以看出ReportViewer相關有12個目錄
我們以Microsoft.ReportViewer.Common目錄為例,切換至Microsoft.ReportViewer.Common目錄後,可以看見二個目錄,以10開頭的目錄為ReportViewer 10,以8開頭的目錄為ReportViewer 8
由於我們使用的是ReportViewer 10,所以我們只要將10.0.0.0__b03f5f7f11d50a3a目錄下的dll檔複製到ASP.NET應用程式的Bin目錄下即可,其餘11個目錄以此類推。

詳細參考可連結

2012年1月26日 星期四

RDLC (一) 簡介

RDLC究竟是什麼?RDLC是Report Definition Language Client-Side的縮寫,為微軟用戶端報表定義語言,可利用Visual Studio 2005/2008/2010建立RDLC報表,那與傳統Reporting Services報表有什麼不同?Reporting Services報表是利用RDL(Report Definition Language)作為報表檔案,兩者之間差異

依建立方式
  • RDL可由Business Intelligence Development Studio的Report Designer建立。
  • RDLC可由 Visual Studio 2005/2008/2010建立。
另外RDL及RDLC皆利用XML結構描述定義,在檔案的結構描述上是相同的,差異的地方在
  • 報表伺服器利用.rdl檔的<Query>項目資訊連接到資料來源。
  • ReportViewer處理.rdlc檔時,會忽略<Query>元素。
接下來我們來看RDLC的運作架構
上方的圖例是以Web開發為例採用Local Mode,ReportViewer Control利用.rdlc檔結合ADO.NET的DataTable資料呈現報表,報表檔(.rdlc)基本上是儲存在Web站台上。
上方的圖例是以Web開發為例採用Remote Mode,ReportViewer Control透過報表伺服器上的報表檔呈現報表,這裡指的報表伺服器是SQL Server Reporting Services,在報表檔(.rdl)裡定義了執行命令及連結資訊。

Local Mode及Remote Mode的差異比較簡述如下表
Local Mode Remote Mode
報表位置 儲放在本機端。 儲放在報表伺服器上。
匯出種類 只能匯出Excel、PDF、Word
以ReportViewer 10.0為例
報表伺服器所支援的所有格式
授權 免費。 需SQL Server License。

ReportViewer版本說明
依開發工具
  • ReportViewer 8.0   => Visual Studio 2005
  • ReportViewer 9.0   => Visual Studio 2008
  • ReportViewer 10.0 => Visual Studio 2010
提供匯出檔案的不同
  • ReportViewer 9.0   => Excel、PDF
  • ReportViewer 10.0 => Excel、PDF、Word
RDLC的優缺點

優點

缺點
  • 有助於分工
    • 系統開發人員根據商業邏輯自資料庫取出資料。
    • 報表設計人員根據報表如何呈現設計。
  • 權限控管彈性。
  • 報表設計簡單。
  • 相較於RDL,RDLC資料的篩選是交由原本的程式處理,學習曲線較短。
  • 免費(本機模式)。
  • 匯出檔案格式較少
    • ReportView   9.0=>Excel、PDF
    • ReportView 10.0=>Exel、PDF、Word
  • 報表檔儲存與管理由各系統個別處理。
  • 無快取及訂閱功能。
  • 設計階段無法預覽報表。

相關參考
將 RDLC 檔轉換為 RDL 檔
SQL Server 2008 R2 Reporting Services報表服務 尹相志、胡百敬 著

2012年1月22日 星期日

Fancybox使用筆記

因為使用者的需求改變希望在點選圖片時,能有AjaxControlToolkit裡的ModalPopup效果,這裡我使用Fancybox達成使用者需求,以下為使用心得的整理,首先我們先下載Fancybox,下載解壓縮後會有個fancybox目錄,目錄裡存放的是fancybox所使用的js檔、css檔及相關圖檔,我們必須將這些引用到我們的站台才能使用fancybox的特效,但通常我們的站台目錄結構會將js檔、css檔及圖檔分別存放在不同的目錄,以下為我範例站台的目錄結構


將fancybox目錄下的jquery.fancybox-1.3.4.pack.js及jquery.mousewheel-3.0.4.pack.js複製到範例站台下的Scripts目錄,css部份將fancybox目錄下的jquery.fancybox-1.3.4.css複製到範例站台下的Styles目錄,圖檔部份則將fancybox目錄下的圖檔全部複製到範例站台下的Image目錄,因為圖檔的位置已變更,所以我們必須修改jquery.fancybox-1.3.4.css,修改分為二個部份,第一個針對url增加../Image/
例如 url('fancybox.png')改為url('../Image/fancybox.png')
主要是以css檔案位置的相對路徑來寫,第二個部份是針對css檔內容含有MS專有濾鏡語法做修改,
例如
{ filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_n.png', sizingMethod='scale'); }
改成
{ filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='Image/fancy_shadow_n.png', sizingMethod='scale'); }
這個部份是以html檔案位置的相關路徑來寫,修改完css內容部份後我們進行html程式碼撰寫,以下為程式碼範例
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>fancybox Demo</title>
    <script type="text/javascript" src="Scripts/jquery-1.5.1.min.js"></script>
    <script type="text/javascript" src="Scripts/jquery.mousewheel-3.0.4.pack.js"></script>
    <script type="text/javascript" src="Scripts/jquery.fancybox-1.3.4.pack.js"></script>    
    <link rel="stylesheet" type="text/css" href="Styles/jquery.fancybox-1.3.4.css"/>
    <script type="text/javascript">
        $(document).ready(function () {
            $("#imgBluehills").fancybox({
                'type': 'image',
                'href': 'Blue hills.jpg'
            });

            $("#imgSunset").fancybox({
                'type': 'image',
                'href': 'ImagePhoto.ashx'
            });

            $("a[rel=imgGroup]").fancybox({
                'transitionIn': 'none',
                'transitionOut': 'none',
                'titlePosition': 'over',
                'titleFormat': function (title, currentArray, currentIndex, currentOpts) {
                    return '<span id="fancybox-title-over">Image ' + (currentIndex + 1) + ' / ' + currentArray.length + (title.length ? ' &nbsp; ' + title : '') + '</span>';
                }
            });
        });
    </script>
</head>
<body>
    <div style="text-align:center">
        <img id="imgBluehills" src="Blue hills_s.jpg" />
        <hr />
        <img id="imgSunset" src="Sunset_s.jpg" />
        <hr />
 <a rel="imgGroup" href="Water lilies.jpg">
            <img src="Water lilies_s.jpg" />
        </a>
 <a rel="imgGroup" href="Winter.jpg">
            <img src="Winter_s.jpg" />
        </a>
    </div>
</body>
</html>
網頁呈現部份主要區分為三個部份,第一個部份是點撃縮圖(Blue hills_s.jpg),顯示儲放在Web app根目錄下的Blue hills.jpg圖檔,第二個部份是點撃縮圖(Sunset_s.jpg),利用Generic Handler(.ashx)取得圖檔,可利用在圖檔存放位置與網頁不同空間時,例如圖檔放在NAS空間,第三個部份將多張圖片以幻燈片效果顯示。
詳盡內容可參考以下連結



2012年1月21日 星期六

從Client端指定WebService路徑

我們在使用其他平台的ASP.NET WebService時,有時會面臨WebService路徑需要改變,像

  1. 開發期間所引用的WebService來自測試主機,但部署到正式環境時,需引用正式環境的WebService。
  2. 因整體公司的IT架構規劃,需將WebService的主機位置做搬移。
每次WebService路徑變更時都要開啟Visual Studio 20xx工具做變更,似乎不是非常聰明的做法,比較好的作法可以利用web.config檔或資料庫儲存WebService路徑,以下是利用web.config方式的範例
web.config部份
<appsettings> 
    <add key="WebServiceUrl" value="http://192.168.1.3/webservice.asmx" />
</appsettings>

程式部份
WebReference.WebService ws = new WebReference.WebService();
ws.Url = System.Configuration.ConfigurationManager.AppSettings["WebServiceUrl"];
Response.Write(ws.HelloWorld());

更詳盡的參考,請點

The test form is only available for requests from the local machine.

最近在測試ASP.NET的WebService功能,遇到了“The test form is only available for requests from the local machine.”訊息(中文版訊息為:測試表單只適用於來自本機電腦的要求。),在查詢過MSDN文件後明白要在web.config做相關設定,預設web.config檔的<webServices>項目在子項目<protocols>只啟用HttpSoap 和 Documentation,只需加上HttpPost即可,以下為程式碼範例

      <webServices>
         <protocols>
            <add name="HttpPost"/>
         </protocols>
      </webServices>

另外在<protocols>項目還有一個HttpGet,HttpPost與HttpGet的不同在HttpPost是網頁必須透過post的方式呼叫WebService,HttpGet則只需知道WebService所提供的方法網址即可,如以下所示
http://192.168.1.3/webservice.asmx/HelloWorld

針對公開WebService後的安全性問題可參考Building Secure Web Services