2015年12月31日 星期四

Javascript 產出XLSX

先前已分享過Javascript產出Excel IE主要是透過ActiveXObject來執行Script,而非IE瀏覽器則是透過HTML的內容假裝是XLS檔案來匯出,若碰到IE被限制ActiveXObject不能執行的狀況就無法正常產出。

因此進一步找了一個JSlib-- js-xlsx (簡單易懂的名稱),是Open Source,可直接透過瀏覽器讀取用戶端的檔案而不需要透過Server 進行。

可讀取的檔案格式:
·      Excel 2007+ XML Formats (XLSX/XLSM)
·      Excel 2007+ Binary Format (XLSB)
·      Excel 2003-2004 XML Format (XML "SpreadsheetML")
·      Excel 97-2004 (XLS BIFF8)
·      Excel 5.0/95 (XLS BIFF5)
·      OpenDocument Spreadsheet (ODS)
寫入的檔案格式:
·      XLSX
·      CSV (and general DSV)
·      JSON and JS objects (various styles)

GitHub:
https://github.com/SheetJS/js-xlsx

lib主要是進行檔案內容的讀取及寫入,若要能下載則需要搭配另外的JS Lib,因此共需要的lib如下。
·         Blob.js  http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js
·         FileSaver.js http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js
·         Export2Excel.jscopy from http://sheetjs.com/demos/table.html
·         xlsx.core.min.jshttps://github.com/SheetJS/js-xlsx

以下提供簡單範例


<table id="export">
    <
tr>
        <
td>Number</td>
        <
td>Name</td>
        <
td>Gender</td>
        <
td>Age</td>
    </
tr>
    <
tr>
        <
td>1</td>
        <
td>Mary</td>
        <
td>Female</td>
        <
td>48</td>
    </
tr>
    <
tr>
        <
td>2</td>
        <
td>Paul</td>
        <
td>Male</td>
        <
td>18</td>
    </
tr>
</
table>
<
button id="exportBtn">Export</button>
<
script>
   
$(document).ready(function(){
      
$("#exportBtn").click(function(){
          
//將需要轉成ExcelTable取出
          
var exportTable = document.getElementById("export");
          
//匯出成Excel,此功能略有調整,原本傳的是ID,改為傳TableElement較方便
          
export_table_to_excel(exportTable,"ExportExcel.xlsx");
       });
    });
</
script>

2015年11月2日 星期一

Javascript產出Excel

有時碰到有些資料表(Data Grid)因為Server Side運算時間很久或資料量大 

便不希望再次由Server端回傳Excel 

而既然瀏覽器端上已經有運算結果的資料,就可直接用來產生Excel檔案,因此找了一個適用於IEChrome的處理方法,紀錄一下。 

其概念在非IE的瀏覽器上,透過HTML<a>將檔案回傳,類似以下的結構 

<a 
download="fileName.xls" href="data:application/vnd.ms-excel;base64,(base64編碼)"></a> 

其中只是將資料表以Html Table的字串方式透過base64編碼。 

而在IE的瀏覽器上則是透過ActiveXObject直接開啟Excel並直接使用Script產出結果,還滿有意思的~可以試試看!(雖然又被IE玩了一次....)

function exportExcel(){
    // Html Table資料
   
var table = "<table>" +
                   
"<tr><th>Name</th><th>Age</th><th>Score</th></tr>" +
                   
"<tr><td>Scott</td><td>13</td><td>90</td>" +
                   
"<tr><td>Tiger</td><td>14</td><td>70</td></tr>" +
               
"</table>";
                 // IE11後的ActiveXObject判斷方式不同...
   
var isIE = window['ActiveXObject'] || "ActiveXObject" in window ? 
               true : false;
   
if (!isIE) {
       
var uri = 'data:application/vnd.ms-excel;base64,'
           
, template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}</table></body></html>'
           
, base64 = function (s) {
               
return window.btoa(unescape(encodeURIComponent(s)))
            }
            , format =
function (s, c) {
               
return s.replace(/{(\w+)}/g, function (m, p) {
                   
return c[p];
                })
            };

       
var ctx = {worksheet: 'Worksheet', table: table.innerHTML};
       
var a = document.createElement('a');
       
a.href= uri + base64(format(template, ctx));
       
a.download=reportMaster.reportName+".xls";
       
a.click();
   
} else {
       
var i, j, str,
           
rowCount = table.rows.length,
            // Activates Excel
           
excel = new ActiveXObject('Excel.Application');
        // Opens a new Workbook
       
excel.Workbooks.Add(); 
        // Shows Excel on the screen
       
excel.Application.Visible = true;
       
for (i = 0; i < rowCount; i++) {
           
for (j = 0; j < table.rows[i].cells.length; j++) {
               
str = table.rows[i].cells[j].innerText;
               
if(str!==null&&str!=='undefined'){
                    // Writes to the sheet
                   
excel.ActiveSheet.Cells(i + 1, j + 1).Value = str
                }
                   
           
}
        }
    }
}


2015年10月28日 星期三

IE11瀏覽器的判斷方式


ActiveXObject undefine

節錄微軟官方的一段說明.....
Starting with IE11, the navigator object supports plugins and mimeTypesproperties. In addition, the window.ActiveXObject property is hidden from the DOM. (This means you can no longer use the property to detect IE11.)

微軟的IE....又改了....IE10之前可以直接判斷window有沒有ActiveXObject來分辨是不是IE瀏覽器,IE11將ActiveXObject改成隱藏....所以不能直接判斷,需要改成 "ActiveXObject" in window 的方式如下....

var isIE=window['ActiveXObject'] ? true :
         "ActiveXObject" in window ? true : false;


紀錄一下....被IE陰了的紀錄....

2015年10月8日 星期四

網頁GZIP壓縮設定


 開發網頁時,若碰到內容較多的表單類或是資料表格(Data table or Data grid),常會讓網頁本身的文字內容就很肥大。

尤其若碰到需要引用一些javascript liberaryextjs or flex這種本身就很肥的東西,常常光基本結構還沒有放入任何內容就已經超過2-3MB了,開發測試時可能還感覺不出來,因為大部分都會在本地端開發,但是只要放在正式環境時就會看的出來影響了。

壓縮HTTP內容(HTTP Compression)


所以如果可以將資料壓縮後再傳送,就可以節省許多頻寬在傳輸上了。如下圖示可看的出來減少了網路所傳輸的資料量,但相對的會增加Server壓縮檔案及Browser解壓縮的消耗。



壓縮的效益


壓縮及解壓縮會消耗ServerBrowserCPU,但設定得宜的話,可以大大減少User等待網路的時間,因此視網站的資料情況設定合適的參數是很重要的。

而影音、圖片及壓縮檔案因大部分已經經過壓縮處理,因此不適合再透過Server做壓縮,會導致增加壓縮成本但網路傳輸量卻減少的有限的反效果。

建議只設定三種格式做壓縮,HTMLCSSJavascript。若透過JSON格式取大量資料的話,也可另外增加application/json的設定將其補上。

Tomcat GZIP Compression設定


找到${tomcat_home} /conf/server.xml,修改Connector的設定

<Connector port="8080" maxHttpHeaderSize="8192"
           maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
           enableLookups="false" redirectPort="8443" acceptCount="100"
           connectionTimeout="20000" disableUploadTimeout="true"
           compression="on"
          
compressionMinSize="2048"
          
noCompressionUserAgents="gozilla, traviata"
          
compressableMimeType="text/html
, text/css, text/xml"/>

屬性說明(Copy From Apache)
compression
The Connector may use HTTP/1.1 GZIP compression in an attempt to save server bandwidth. The acceptable values for the parameter is "off" (disable compression), "on" (allow compression, which causes text data to be compressed), "force" (forces compression in all cases), or a numerical integer value (which is equivalent to "on", but specifies the minimum amount of data before the output is compressed). If the content-length is not known and compression is set to "on" or more aggressive, the output will also be compressed. If not specified, this attribute is set to "off".
Note: There is a tradeoff between using compression (saving your bandwidth) and using the sendfile feature (saving your CPU cycles). If the connector supports the sendfile feature, e.g. the NIO connector, using sendfile will take precedence over compression. The symptoms will be that static files greater that 48 Kb will be sent uncompressed. You can turn off sendfile by setting useSendfile attribute of the connector, as documented below, or change the sendfile usage threshold in the configuration of the DefaultServletin the default conf/web.xml or in the web.xml of your web application.
compressableMimeType
The value is a comma separated list of MIME types for which HTTP compression may be used. The default value istext/html,text/xml,text/plain,text/css,text/javascript,application/javascript .
compressionMinSize
If compression is set to "on" then this attribute may be used to specify the minimum amount of data before the output is compressed. If not specified, this attribute is defaults to "2048".
noCompressionUserAgents
The value is a regular expression (using java.util.regex) matching the user-agentheader of HTTP clients for which compression should not be used, because these clients, although they do advertise support for the feature, have a broken implementation. The default value is an empty String (regexp matching disabled).
  

Weblogic GZIP Compression設定

2.      把下載的jar放到${web_appliaction_home}/WEB-INF/lib
3.      開啟${web_appliaction_home}/WEB-INF/web.xml加入filter的設定
<filter>
    <filter-name>CompressingFilter</filter-name>
    <filter-class>com.planetj.servlet.filter.compression.CompressingFilter</filter-class>
    <init-param>
        <param-name>includeContentTypes</param-name>
        <param-value>text/html,text/css,application/x-javascript</param-value>
    </init-param>
    <init-param>
        <param-name>compressionThreshold</param-name>
        <param-value>256</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>CompressingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

參考資料:

2015年8月12日 星期三

ORA-22922: nonexistent LOB value

ORA-22922: nonexistent LOB value

最近處理了一個原本以為是Java端控制JDBC資料有問題而導致的ORA錯誤,找了半天終於找到可能的原因,紀錄一下免得下次再找半天....

環境敘述

Database : Oracle 10G
Program Language : Java
Application Server : weblogic
Framework : spring 4


錯誤訊息

org.springframework.jdbc.UncategorizedSQLException: StatementCallback;
uncategorized SQLException for SQL [ 
  SELECT *
  FROM (SELECT a.*, ROWNUM r__
          FROM (SELECT *
                  FROM DUMMY_V
                 WHERE (1 = 1)) a
         WHERE ROWNUM < ( (1 * 100) + 1))
  WHERE r__ >= ( ( (1 - 1) * 100) + 1)
];
SQL state [99999]; error code [22922]; ORA-22922: nonexistent LOB value;
nested exception is java.sql.SQLException: ORA-22922: nonexistent LOB value


錯誤分析

其中DUMMY_V是一個View,透過WMSYS.WM_CONCAT將多筆資料合成一筆,為了避免處理CLOB資料型態,所以公司習慣補上to_char將資料轉成varchar,因此寫成了

... select key, to_char(WMSYS.WM_CONCAT(column1 )) from table ...

因此在某些狀況下(可能是資料筆數少),會出現錯誤訊息。原因可能是因為google上找到的某段訊息....

to_char and WM_CONCAT allocated memory size is inconsistent

所以將to_char拿掉後老實處理就解決了.....

資料參考
http://www.javaproblemstips.com/160064/

更新(2015/9/25):
拿掉之後仍在資料較多的情況發生了同樣的訊息,因此後來將本來透過wm_concat將資料串接改成透過trigger串街後存入其他的資料表在查詢的方式處理,因此上述的方法可能沒有完全解決問題,透過11G的LISTAGG會是較好的處理方式(若只有10G以前的版本就.....)

2015年7月8日 星期三

Could not install Gradle distribution from 'https://services.gradle.org/distributions/gradle-xxx-all.zip'


最近在研究Gradle,在公司因為Porxy問題一直撞牆.....想使用Eclipse來用Gradle出現以下錯誤一直找不出原因

Could not install Gradle distribution from 'https://services.gradle.org/distributions/gradle-2.1-all.zip'

要排除這個問題需要以下步驟:
  1. 先去下載所需的Gradle版本(https://gradle.org/gradle-download/)
  2. 解壓縮到任意目錄(這裡是D:\Tools\gradle\gradle-2.4)
  3. 只需要將Eclipse的Gradle Distribution Folder設為所下載的Gradle目錄即可....



2015年2月24日 星期二

Uploadify導致Chrome掛點問題~~(Uploadify Chrome Crash)

紀錄一下工作上遇到的狀況。

有個jQuery上傳多檔案的套件Uploadify,在Chrome會時不時的出現錯誤畫面。


原因是以下這段Code在初始化的時候會發生問題

$("#uploadify_id").uploadify({})...
找到兩個解法
  1. 在import js的時候補上隨機變數,讓瀏覽器每次重新載入js而不使用暫存。
    • <script src="../jquery.uploadify.min.js?ver=<%= Math.round( Math.random() * 10000 ) %> " type="text/javascript"/>
  2. 將Code以setTimeout方式延遲至所有js執行完後再運行。
    1. setTimeout(function () {
          $("#uploadify_id").uploadify({})...
      },0);
實際運行後以第2個方法能正常運行,第一個方法應是已經過時。

而setTimeout(fn,0)的解釋可參考http://pandacafe.net/blog/337

問題發生的可能原因有說是session的,也有說是cookie的,但我猜可能跟swf有關係,也許用HTML5的元件就不會發生這問題了吧?

總之記錄一下囉。