Pages

  • Twitter
  • Facebook
  • Google+
  • RSS Feed

2013年3月16日 星期六

Demo網頁:
http://myimagewall.no-ip.org:8080/sessionWeb/index.jsp

設置Filter順序 注意與gzipFilter的順序
順序錯誤將會先進行壓縮才打上浮水印
此時將會無法讀取已經過壓縮的圖檔 而導致動態加載浮水印失敗

觀念圖片:


在web.xml中的設定:

  <filter>
      <filter-name>gzipFilter</filter-name>
      <filter-class>com.helloweenvsfei.gzip.GZipFilter</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>gzipFilter</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>
 
 
  <filter>
      <filter-name>waterMarkFilter</filter-name>
      <filter-class>
          com.helloweenvsfei.watermark.WaterMarkFilter
      </filter-class>
      <init-param>
          <param-name>waterMarkFile</param-name>
          <param-value>/logo.png</param-value>
      </init-param>
  </filter>
  <filter-mapping>
      <filter-name>waterMarkFilter</filter-name>
      <url-pattern>*.jpg</url-pattern>
  </filter-mapping>

以下為浮水印Filter的實作:


接口WaterMarkFilter.java:

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class WaterMarkFilter implements Filter {

    // 水印圖片,設定在初始化參數中
    private String waterMarkFile;
   
   
    public void init(FilterConfig config) throws ServletException {
        String file = config.getInitParameter("waterMarkFile");
        waterMarkFile = config.getServletContext().getRealPath(file);
        System.out.println(waterMarkFile);
    }

    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
       
        System.out.println("執行浮水印過濾器");
        // 自定義的response
        WaterMarkResponseWrapper waterMarkRes = new WaterMarkResponseWrapper(response, waterMarkFile);

        chain.doFilter(request, waterMarkRes);

        // 打水印,輸出到客戶端瀏覽器
        waterMarkRes.finishResponse();
    }

    public void destroy() {
    }

}

WaterMarkResponseWrapper.java為自訂的類別,為了overwrite getOutputStream():

import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import com.helloweenvsfei.util.ImageUtil;

public class WaterMarkResponseWrapper extends HttpServletResponseWrapper {

    // 水印圖片位置
    private String waterMarkFile;
   

    // 原response
    private HttpServletResponse response;

    // 自定義servletOutputStream,用於緩衝影像資料
    private WaterMarkOutputStream waterMarkOutputStream;

    public WaterMarkResponseWrapper(HttpServletResponse response,String waterMarkFile) throws IOException {
        super(response);
        this.response = response;
        this.waterMarkFile = waterMarkFile;
       
        this.waterMarkOutputStream = new WaterMarkOutputStream();
    }

    // 覆蓋getOutputStream(),傳回自定義的waterMarkOutputStream
    public ServletOutputStream getOutputStream() throws IOException {
        return waterMarkOutputStream;
    }

    public void flushBuffer() throws IOException {
        waterMarkOutputStream.flush();
    }

    // 將影像資料打水印,並輸出到客戶端瀏覽器
    public void finishResponse() throws IOException {

        // 原圖片資料
        byte[] imageData = waterMarkOutputStream.getByteArrayOutputStream().toByteArray();
       
        // 打水印後的圖片資料
        byte[] image = ImageUtil.waterMark(imageData, waterMarkFile);
       
       
        // 將影像輸出到瀏覽器
        response.setContentLength(image.length);
        response.getOutputStream().write(image);

        waterMarkOutputStream.close();
    }
}


WaterMarkOutputStream.java實作複寫的類別:

package com.helloweenvsfei.watermark;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import javax.servlet.ServletOutputStream;

public class WaterMarkOutputStream extends ServletOutputStream {

    // 緩衝圖片資料
    private ByteArrayOutputStream byteArrayOutputStream;

    public WaterMarkOutputStream() throws IOException {
        byteArrayOutputStream = new ByteArrayOutputStream();
    }

    public void write(int b) throws IOException {
        byteArrayOutputStream.write(b);
    }

    public void close() throws IOException {
        byteArrayOutputStream.close();
    }

    public void flush() throws IOException {
        byteArrayOutputStream.flush();
    }

    public void write(byte[] b, int off, int len) throws IOException {
        byteArrayOutputStream.write(b, off, len);
    }

    public void write(byte[] b) throws IOException {
        byteArrayOutputStream.write(b);
    }

    public ByteArrayOutputStream getByteArrayOutputStream() {
        return byteArrayOutputStream;
    }

}

ImageUtil.java利用JDK的影像處理類別動態打上浮水印

package com.helloweenvsfei.util;

import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

import java.awt.MediaTracker;

public class ImageUtil {

    /**
     *
     * @param imageData
     *            JPG 影像檔案
     * @param waterMarkFile
     *            水印圖片
     * @return 加水印後的影像資料
     * @throws IOException
     */
    public static byte[] waterMark(byte[] imageData, String waterMarkFile)
            throws IOException {

        // 水印圖片的右邊距 下邊距
        int paddingRight = 10;
        int paddingBottom = 10;
       
       
        // 原始影像
        Image image = new ImageIcon(imageData).getImage();
        int imageWidth = image.getWidth(null);
        int imageHeight = image.getHeight(null);
               
        // 水印圖片
        Image waterMark = ImageIO.read(new File(waterMarkFile));
        int waterMarkWidth = waterMark.getWidth(null);
        int waterMarkHeight = waterMark.getHeight(null);
          
        // 如果圖片尺寸過小,則不打水印,直接傳回
        if (imageWidth < waterMarkWidth + 2 * paddingRight || imageHeight < waterMarkHeight + 2 * paddingBottom)
        {
            return imageData;
        }
       
        BufferedImage bufferedImage = new BufferedImage(imageWidth,imageHeight, BufferedImage.TYPE_INT_RGB);

        Graphics g = bufferedImage.createGraphics();

        // 繪製原始影像
        g.drawImage(image, 0, 0, imageWidth, imageHeight, null);
        // 繪製水印圖片
        g.drawImage(waterMark, imageWidth - waterMarkWidth - paddingRight,
                imageHeight - waterMarkHeight - paddingBottom, waterMarkWidth,
                waterMarkHeight, null);
        g.dispose();

        // 轉成JPEG格式
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
        encoder.encode(bufferedImage);
        byte[] data = out.toByteArray();
        out.close();
        return data;
    }
}

2013年3月11日 星期一

剛剛PTT JAVA版的問題
今天剛好有摸到

問題
如何用Apache log4j紀錄伺服器資訊
且記錄檔檔名格式為每日一個log檔
如:20130313_log.txt

我是用最新的函示庫 你要有
commons-logging-1.1.1.jar
這個一般API幫你處理在你不確定紀錄方式的時候
log4j-1.2.17.jar
log4j的本體
兩個丟進/WEB-INF/lib/底下

寫一個Filter紀錄訊息
LogFilter.java:

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class LogFilter  implements Filter {
   
    private Log log = LogFactory.getLog(this.getClass());
    private String filterName;
   
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub
        filterName =  filterConfig.getFilterName();
        log.info("啟動Filter:" + filterName);
    }
   
    @Override
    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {
        // TODO Auto-generated method stub
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
       
        long startTime = System.currentTimeMillis();
        String requestURI = request.getRequestURI();
       
        requestURI = request.getQueryString() == null ? requestURI : (requestURI + "?" + request.getQueryString());
        chain.doFilter(request, response);
       
        long endTime = System.currentTimeMillis();
        log.info(request.getRemoteAddr() + "存取了" + requestURI + ",總用時" + (endTime - startTime) + "毫秒,");
       
    }


    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        log.info("關閉Filter:" + filterName);
    }

}

web.xml中Filter的設定和網址mapping
<filter>
      <filter-name>LogFilter</filter-name>
      <filter-class>LogFilter</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>LogFilter</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>

關鍵的log4j.properties:
通常放在classes資料夾下

log4j.rootLogger=INFO, day

#org.apache.log4j.DailyRollingFileAppender每日一個紀錄檔
log4j.appender.day=org.apache.log4j.DailyRollingFileAppender
log4j.appender.day.layout=org.apache.log4j.PatternLayout
#記錄檔內容
log4j.appender.day.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss,SSS} [%l]-[%p] %m%n
#記錄檔路徑
log4j.appender.day.File=C:\\system_notice.log
#隔日備份時按照日期更改記錄檔檔名
log4j.appender.day.DatePattern='.'yyyyMMdd

完工

2013年2月18日 星期一



初觸MVC架構:

什麼是MVC?
http://zh.wikipedia.org/wiki/MVC

資訊業 如果走網路這條路 走Web App
一定遲早會碰到的觀念
很多套件或是Framework會標榜這個名詞
但是他卻不是一個程式 而是一種design pattern
是一種的"架構"觀

Joomla也是採用這種設計模式

打得比方:
一般網頁PHP也好 JSP也罷
初學者都容易把html css js php都寫在一個
file.php頁面
裏頭還可能包含了
直接對MySQL等DB下SQL語法操作的動作
例如:

<?php
include("linkmysql.php");
$sql = "update member set passwordhash = '$hash' where account = '$account'";
$row = mysql_query($sql);
?>

<html>
<head>
....

等等
在MVC的架構裡面 這種寫法是最要不得的
1.程式耦合性高:
改一點地方 可能其他檔案都要改
專案一大起來 會搞砸的

2.程式重用性低:
可能1.php 2.php 3.php中全部都要跟MySQL溝通
就要寫三次建立連線的語法

3.基於以上兩點,不易維護


4.不夠安全
頁面直接與後端DB溝通,有被攻擊的疑慮

實作一個PHP Web App
來比較跟傳統硬幹的Web App有哪裡不一樣

第一支Apache PHP Web App程式
使用codeigniter framework

可以到我的github下載
https://github.com/BenjaminLu/CodeIgniter_FirstMVC

下載完畢丟進Apache www資料夾下
(之前有專案建議新開資料夾丟入)
瀏覽器網址輸入
Localhost/沒有新開資料夾丟入則網址沒有此段/Hello
效果應該會等同於
http://myimagewall.no-ip.org/Hello


內部包含了









 




application資料夾下包含了程式
MVC三個模組
分別是

controllers
models
views
三個資料夾

這隻範例新增在整個

CodeIgniter 框架中的檔案只有
controllers資料夾中的Hello.php

views資料夾下的hello_world.php

以及根目錄下的

.gitattributes

.gitignore

README.md

 以上三個是github網站的說明檔 你可以無視他

.htaccess 裡面透過rewrite隱藏與伺服器的接口index.php
這樣在網址列就不需要輸入index.php

其他全部都是屬於最新版CodeIgniter framework的預設檔案

如你所見 此專案沒有使用到Model層的DB操作

重點在Control層與View層間的運作

 








 

在system\libraries裏頭framework已經幫你放置了常用的功能函示庫
包含E-mail 加解密 上傳 Session Log Ftp等等 包成類別
也提供使用類別中Method的方法 各類別請參閱官方文件

其他更清楚的說明可以觀看
延伸閱讀:
http://blog.wu-boy.com/2012/02/intrduction-to-codeigniter-model-view-controller/


data flow的運行如下:
你從網址輸入

http://YourDomain/Hello


Enter的同時會發送Get訊息給根目錄下的index.php

不過index.php被.htaccess檔案所隱藏了 所以你並不需要在網址列輸入

http://YourDomain/index.php/Hello
不過意思是一樣的

index.php接收到網址傳過來的參數
一般格式為

http://YourDomain/index.php/Class/

表示index.php接受到資訊知道要移交request給Hello控制層
並且在此例中 不夾帶方法和參數


於是程式就執行到了Hello.php
如果你的控制層副檔名不是.php 那麼在網址中就不能省略最後副檔名


回到重點打開controllers底下的Hello.php可以看到這樣一段程式碼

 <?php
class Hello extends CI_Controller {
    public function __construct()
    {
        parent::__construct();
    }
    public function index()
    {
        $data['title'] = "Hello, World!";
        $data['heading'] = "My First MVC Web App";
        $data['hellostring'] = "Hello, MVC!";
        $this->load->view('hello_world',$data);
    }   
}
?>


目前index.php已經移交request給Controller 那麼我們的Hello.php便是控制層的檔案
他做的事情就是控制 並把參數設置好 移交給View層

是不是就是你學過的物件導向的觀念呢?

來看看Controller是怎麼做到控制的

class Hello extends CI_Controller
表示Hello類別 繼承了CI_Contriller的類別

因此他也會繼承CI_Controller中的方法


  public function __construct() {
        parent::__construct();
  }


表示他的建構元與父類別CI_Controller相同


public function index() {
        $data['title'] = "Hello, World!";
        $data['heading'] = "My First MVC Web App";
        $data['hellostring'] = "Hello, MVC!";
        $this->load->view('hello_world',$data);

} 

function index通常是預設載入
也就是你沒有在網誌列輸入

http://YourDomain/index.php/Class/Method
的Method 他麼他預設就會執行index() function
若是在class Hello中 新增一個method
public function comments() {
        echo 'Look at this!';

}
那麼要呼叫這個method 網址就要輸入
http://YourDomain/index.php/Hello/comments

是不是對控制層稍微有一點了解了呢?
 那麼他是怎麼與View層溝通將頁面透過瀏覽器 吐給使用者呢?

重點就是index() function中的此行
$this->load->view('hello_world',$data);
表示載入一個view層物件"hello_world"並且傳送參數$data陣列給view層

而$data陣列中陸續放了三個字串
$title = "Hello, World!";
$heading = "My First MVC Web App";
$hellostring = "Hello, MVC!";

分別在$data[0] ,$data[1] ,$data[2]的意思

控制層結束以後來看看View層是怎麼處理接收到的資料並且顯示在瀏覽器上的

打開views資料夾底下的hello_world.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="zh-TW">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />   
    <title><?php echo $title;?></title>
</head>
<body>
<h1><?php echo $heading;?></h1>
<p><?php echo $hellostring;?></p>
</body>
</html>


就是一個很簡單的html表單
但是中間加入了三段PHP語法 分別在

<title><?php echo $title;?></title>

<h1><?php echo $heading;?></h1>

<p><?php echo $hellostring;?></p>


這表示在title的標籤中(瀏覽器的分頁標籤名稱)
透過php輸出接收到的$title變數 (Contriller傳遞過來的參數 此例為: Hello, World!) 
$heading
$hellostring 皆同上原理

如此以來這樣的設計 能對程式設計師 帶來什麼樣的好處呢?

1.可維護性
若是今天我想要更改顯示的資料
我只需要在Control層更改傳遞的變數值即可 

2.耦合性低
各層間沒有相依性,不會因為改變一個檔案而要去修改很多相關的檔案

3.重用性高
若是另外一個View檔案也需要相同的參數,我們只需要在Control層中也傳遞參數給
另一個View檔案



4.安全性高
利用framework操作代替直接與後端以及DB溝通,過濾了SQL Injeciton

透過framework需要編寫的程式碼也少了非常多
就能做到一樣的效果 只要架構建構好
之後開發速度都會比以往一個功能一個頁面寫來的快很多


在台灣老闆和客戶喜歡改來改去吃到飽的環境下
一個專案的可維護性相當重要 有了好的維護性 自然功能擴充性也不會太差

架構好是非常重要的 
我想這也是為什麼SCEA會是最高證照
而SCJP是初階證照的原因
Related Posts Plugin for WordPress, Blogger...
 
© 2012. Design by Main-Blogger - Blogger Template and Blogging Stuff