2014年12月15日 星期一

hibernate envers

Hibernate Envers 提供數據變更時, 進行自動的 audit 記錄, 但如果需要額外的資訊時, 需要一些客製的程式.

DB : Oracle
DOC : http://docs.jboss.org/envers/docs/#revisionlog
Bean 加上 annotation : @Audited, hibernate 會新增兩個 table,
1. REVINFO : 記錄版本號
2. Table Name_AUD 結尾(以下為例 : Table 會增加一個 TEST_AUDIT_AUD)

@Audited
@Entity
@Table(name = "TEST_AUDIT")
public class TestAudit implements java.io.Serializable {

    private static final long serialVersionUID = 1231323352543L;

    @Id
    @SequenceGenerator(name = "TEST_AUDIT_ID_SEQ", allocationSize = 1, initialValue = 1,
                    sequenceName = "TEST_AUDIT_ID_SEQ")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TEST_AUDIT_ID_SEQ")
    @Column(name = "ID")
    private Long id;

    @Column(name = "TITLE", nullable = false, length = 128)
    private String title;

    @Column(name = "DESCRIPTION", nullable = true, length = 256)
    private String description;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

}

但有時候我們需將 user info 也記錄起來, 此時會需要一個自定的 REVINFO TABLE, 官網文件中提供 2 個方法, 這裡採用不  extends 的方式.
我們需要 2 個 class :
1. 客製的 REVINFO :

@Entity
@RevisionEntity(ExampleListener.class)
public class ExampleRevEntity {
    @Id
    @GeneratedValue
    @RevisionNumber
    private int id;

    @RevisionTimestamp
    private long timestamp;

    private String username;

    // Getters, setters, equals, hashCode ...

}

public class ExampleListener implements RevisionListener {
    public void newRevision(Object revisionEntity) {
        ExampleRevEntity exampleRevEntity = (ExampleRevEntity) revisionEntity;
        User user = xxx.getUser(); // 補充1

        exampleRevEntity.setUsername(identity.getUsername());
    }
}

在 spring-mvc 中, 如果 user 的資訊有存在 session 裡, 可以透過

ServletRequestAttributes srAttrs = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
User user = srAttrs.getRequest().getSession().getAttribute("SESSION_KEY_USER");
logger.info("user name is {}", user.getName());

要使用 spring 的 RequestContextHolder (ThreadLocal) 必須在 web.xml 中加入
web.xml : add request listener
<listener>
  <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>


2013年1月25日 星期五

Social with google

此篇日期為 2013.01.25 , 以下為已有 domain 的情況下.

1. 準備篇 : 請先參考 social with yahoo
2. 一樣先來建立一個 project : (如果你還沒有建立過的話, 有的就跳過這個步驟)
    來這裡吧 :   google api console


請點選 : Create Project, 然後將 Groups Migration API 打勾



然後回到頁面頂端, 會發現多了一個功能 : API Access, 點下去

到這裡基本上你已經可以用 google 提供的 key 來做很多事情, 像 search by image, text...等
但我們要使用 oauth 來取得 user profile 與 contacts list 的話, 必須自己去申請 consumer key & secret


輸入 domain (www.pluzme.com) 後, 按下右邊的 Add domain , 下方 Manage registration 的地方會出現你的 domain(www.pluzme.com), 點下去後, 依照畫面的方法來驗證, 跟 yahoo 的很像, 但 google 直接讓你下載一個 html 檔, 直接放到 ap 裡面

記得1~3好了之後, 才按驗證唷, 成功的話會出現 consumer_key & secret


這時候, 只要拿 consumer key & secret 加入到 property 中
www.google.com.consumer_key = www.xxx.com
www.google.com.consumer_secret = n8xxxxxxxxxxxxxxxxxxxxxYF

然後程式只要改一個地方就好, 就是把 "yahoo" 改成 "google" 即可

returnUrl = manager.getAuthenticationUrl("google", returnToUrl);

搞定, 收功

2013年1月24日 星期四

Social with yahoo

直接進入主題, 如何串接 yahoo 與 google , 並取得使用者資訊與 contacts , 以 OAuth 為例

此篇日期為 2013.01.25 , 以下為已有 domain 的情況下.

1. 申請帳號, 如果沒有的話先申請一個(google, yahoo)

2. 申請好後開始建立 project
   A. yahoo :
      a. 連到 http://developer.yahoo.com
      b. 點右上角你的名字, 會有個下拉選單, 選  My Projects


      c. 按 Create Project


      d. 接下來, 對照一下照片, 1~8 如下說明 :     
  
        1. 應用程式名稱 : ex : pluzme
        2. 你的應用程式類型 : ex: Web-based (網頁)
        3. 描述 : ex: 代購網站
        4. 你的網址 : http://www.pluzme.com
        5. 有 favicon 的話就寫一下 : ex : http://design.pluzme.com/images/favicon.ico
        6.  http://www.pluzme.com
        7. 通常選第一個(第一個有包含第二個)
        8. 打勾同意
        9. 送...出..

      e. 接下來, 注意圖中紅色, 藍色與紫色的框起來的地方


      紅色 : 等下程式會用的到資訊, 也不要外流
      藍色 : 必須驗證你的 domain, 這個很重要也一定要做
      紫色 : 要使用功能, 像取得  user 的聯絡人, 資料, 可讀可寫 (自己選擇一下, 選好後下方按 save)
      接下來處理藍色的地方 : 請按 Verify Domain


    
      如果內容沒有 domain, 請按右上方的 Add Domain
      如果有 domain, 請選擇你使用的 Domain 並按下 Domain 右邊的 Verify


      點了之後會跳出一個畫面(如上圖)
      1. 產生一個 html 檔案, 名稱與上面的一樣, 點 file name 可以直接覆製, 如果 server 是 linux 可以用 touch xxxxxxxxxxxxx--.html
      2. 將這個 html 放到網站根目錄, 要可以連的到, 且要是上面的網址的路徑, 要一模一樣
        PS: 這裡我是放在真的可以連的到的 domain 才行, 本機改 host 也不行
      3. 都設定好後, 就按 Verify Domain 吧, 然後會看到, 成功的畫面, 此時回到剛那裡會看到你的 domain 右邊有個 綠色的勾勾


    如果你跟我一樣做到這裡, 代表你已經成功了, 接下來就剩下程式的地方, 基本上不管你是用什麼語言, 上面的步驟是一樣的, 實作的語言就因人而異囉.


程式其實還真的很簡單, 請先到這裡 http://code.google.com/p/socialauth/ 看一下介紹, 要看唷, 看完後再下載 library 使用, google code 上已經有很完整的範例跟教學, 我這裡也來中文化一下 : 

進到首頁看了一些說明後, 下方的這張圖表我覺得很重要, 而這個套件也幫你解決了中間那一段的流程



轉貼來源 : http://code.google.com/p/socialauth/

簡單的說明就是 : 當 user 發 request 要使用 yahoo 登入到你的統統時, server端會先取得要認證的 yahoo 連結, 然後 redirect 過去, 當使用者同意後, yahoo 會 call back 回你的 server 並代一些參數給你, 此時你可以透過這些參數, 再去詢問 yahoo 並取得資料, 有了資料後就看自己想要做什麼

我們來點一下 wiki, 會看到這裡提供了許多範例 : spring, struts1, servlet, seam...等, 

首先有個要注意的是, socialauth 這個套件需要一個  property 檔案, 內容可以參考http://code.google.com/p/socialauth/wiki/SampleProperties

記得把 api.login.yahoo.com.consumer_key, api.login.yahoo.com.consumer_secret 換成剛申請好的 consumer_key 與 consumer_secret

接下來直接進入程式

以 struts2 為例, 當 yahoo 點選 yahoo 登入時, 我假設是連到 http://xxx/yl.do

  @Action(value = "yl", results = { @Result(type = "redirect", name = "redirect", location = "${returnUrl}") })
  public String yl() throws Exception {
    SocialAuthManager manager = null;
    if (sessionMap.containsKey("socialAuthManager")) { // sessionMap 是 struts2 存取 session 用的
      manager = (SocialAuthManager) sessionMap.get("socialAuthManager"); // 如果有就從 session 抓
    } else {
      InputStream in = IndexYahooAction.class.getClassLoader().getResourceAsStream("oauth_consumer.properties"); // 沒有的話就撈設定檔進來
      SocialAuthConfig conf = SocialAuthConfig.getDefault();
      conf.load(in);
      manager = new SocialAuthManager();
      manager.setSocialAuthConfig(conf);
      sessionMap.put("socialAuthManager", manager);
    }
    String tempReturnUrl = "http://www.pluzme.com/yo.do"; // 設定當 success 時, yahoo 要 call back 的 url
    returnUrl = manager.getAuthenticationUrl("yahoo", tempReturnUrl);
    log.info("returnUrl : " + returnUrl);
    return "redirect"; // 然後幫 user redirect 過去
  }

導過去後, 會看到像這樣的長像



這時候, 當按下同意後, yahoo 會導回去剛設定的 call back url : http://www.pluzme.com/yo.do
  @Action(value = "yo", results = { @Result(type = "redirect", name = "success", location = "${returnUrl}") })
  public String yo() throws Exception {
    SocialAuthManager manager = null;
    if (sessionMap.containsKey("socialAuthManager")) {
      manager = (SocialAuthManager) sessionMap.get("socialAuthManager"); // 從 session 抓取剛的 object
    }
    if (manager != null) {
      List<Contact> contactsList = new ArrayList<Contact>();
      Profile profile = null;
      try {
        Map<String, String> paramsMap = new HashMap<String, String>();
        for (Map.Entry<String, String[]> entry : request.getParameterMap()
            .entrySet()) {
          String key = entry.getKey();
          String values[] = entry.getValue();
          log.info(key + ", " + Arrays.toString(values));
          paramsMap.put(key, values[0].toString()); // Only 1 value is
        }
        AuthProvider provider = manager.connect(paramsMap); // 把參數傳進去, 主要是用 oauth_token 取得資訊
        
        profile = provider.getUserProfile(); // 個人資料
        log.info(ToStringBuilder.reflectionToString(profile));

        contactsList = provider.getContactList();
        if (contactsList != null && contactsList.size() > 0) {
          for (Contact p : contactsList) {
            if (StringUtils.isEmpty(p.getFirstName()) && StringUtils.isEmpty(p.getLastName())) {
              p.setFirstName(p.getDisplayName());
            }
            //log.info(ToStringBuilder.reflectionToString(p)); // contacts list
          }
        }
      } catch (Exception e) {
        log.error(e.getMessage(), e);
      } finally {
        sessionMap.remove("socialAuthManager");
      }     
    }

    // 後續自己處理了...
    returnUrl = "http://www.pluzme.com/";
    return SUCCESS;
  }

大概就是這樣囉, 下回再分享 google 申請時要注意的地方, 但程式的地方只需修改一個部份, 其它好像都一樣, 也許可以寫個 util 包起來之類的.

2012年10月29日 星期一

Java-image 高解析縮圖-ImageMagic 與 im4java 的蟹逅

最近在寫處理圖的程式, 在估哥的協助下, 找到了一套好用的程式!
ImageMagick + im4java (java jar)

如果你不喜歡用jdk提供的方式來轉圖, 縮圖, 切圖...等
可以使用這套, 還挺好懂的, im4java 官網的教學也寫的很清楚

ImageMagick 可以換成 GraphicsMagick , 其2種的差別就不在這裡說囉. 反正大家都說 GraphicsMagick 好, 但因為目前我是裝 ImageMagick 所以就以這個為例

PS: jmagick 本身的 setQuality 有問題, 這也是為什麼要改用 im4java 的原因 安裝方式 :

 - ImageMagick : http://www.imagemagick.org/script/binary-releases.php#windows
  32 or 64 的差別 (for windows)
  linux 的話, 看你系統是什麼. centos 的話就yum...等
 - download im4java.jar
  set it to your classpath,

使用時, 比較要注意的地方是
 - window 下, 要設定 ConvertCmd.setGlobalSearchPath(imageMagick 安裝路徑);
  但是在 linux 下則不用, 所以可以使用 jakarta 的 jar (lang.jar)來判斷是什麼 OS

  static {
    if (SystemUtils.IS_OS_WINDOWS) {
      ConvertCmd.setGlobalSearchPath("C:/Program Files/ImageMagick-6.8.0-Q8");
    }
  }

 下面提供範列的程式, 要注意的是有些是我自己用來動態產生 temp 圖要放哪裡的程式, 可以乎略, 另外處理圖時, 通常會丟一個 inputstream 來處理, 則必須去看一下 pipe 怎麼使用.
public class ImageUtils {
  static {
    if (SystemUtils.IS_OS_WINDOWS) {
      ConvertCmd.setGlobalSearchPath(ClubBuyConstants.IMAGEMAGICKPATH);
    }
  }
  private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);

  /**
   * 判斷原來圖片的高,有沒有比要縮的高度還高
   * 
   * @param srcImage
   * @param width
   * @return
   * @throws IOException
   */
  public boolean checkResizeByHeight(String srcImage, int height)
      throws IOException {
    BufferedImage bf = ImageIO.read(new File(srcImage));
    int oriHeight = bf.getHeight();
    if (oriHeight <= height) {
      return false;
    }
    return true;
  }

  /**
   * 依寬縮放
   * 
   * @param oriIn
   * @param newWidth
   * @param toFileName
   * @throws IOException
   */
  public static void resizeImageWithNewWidth(File oriIn, int newWidth,
      String toFileName) throws IOException {
    resizeImageWithNewWidth(new FileInputStream(oriIn), newWidth, toFileName);
  }
  
  /**
   * 依寬縮放
   * 
   * @param oriIn 
   * @param newWidth 最長寬
   * @param toFileName
   * @throws IOException
   */
  public static void resizeImageWithNewWidth(InputStream oriIn, int newWidth,
      String toFileName) throws IOException {
    checkDirs(toFileName);
    FileOutputStream fout = null;
    try {
      IMOperation op = new IMOperation();
      op.addImage("-");

      // 取得來源寬與高
      BufferedImage image = ImageIO.read(oriIn);
      int imageWidth = image.getWidth();
      int imageHeight = image.getHeight();

      // 算出最後要縮的寬與高
      int currentWidth = 0;
      int currentHeight = 0;
      if (imageWidth < newWidth) {
        currentWidth = imageWidth;
        currentHeight = (imageWidth * imageHeight) / imageWidth;
      } else {
        currentWidth = newWidth;
        currentHeight = (newWidth * imageHeight) / imageWidth;
      }
      // 設定 quality(圖太小的話就設高一點, 以免不清楚)
      if (currentWidth <= 350 || currentHeight <= 350) {
        op.quality(95d);
      } else {
        op.quality(85d);
      }

      op.resize(currentWidth, currentHeight);
      op.addImage("jpg:-");

      fout = new FileOutputStream(new File(toFileName));
      ConvertCmd convert = new ConvertCmd();
      Pipe pipeIn = new Pipe(oriIn, null);
      Pipe pipeOut = new Pipe(null, fout);
      convert.setInputProvider(pipeIn);
      convert.setOutputConsumer(pipeOut);
      convert.run(op);
    } catch (InterruptedException e) {
      log.error(e.getMessage(), e);
    } catch (IM4JavaException e) {
      log.error(e.getMessage(), e);
    } finally {
      IOUtils.closeQuietly(oriIn);
      IOUtils.closeQuietly(fout);
    }
  }

  /**
   * 切圖, 並回傳 temp file, 後續自己處理
   * 
   * @param oriFile
   * @param x
   * @param y
   * @param toX
   * @param toY
   * @return
   * @throws Exception
   */
  public static File cropImgToTemp(File oriFile, int x, int y, int toX, int toY)
      throws Exception {
    return cropImgToTemp(new FileInputStream(oriFile), x, y, toX, toY);
  }
  
  /**
   * 切圖, 並回傳 temp file, 後續自己處理
   * 
   * @param oriIn 原始檔案
   * @param x 要切圖的 x 坐標(開始)
   * @param y 要切圖的 y 坐標(開始)
   * @param toX 要切圖的 x 坐標(結束)
   * @param toY 要切圖的  y 坐標(結束)
   * @return file (temp file)
   * @throws Exception
   */
  public static File cropImgToTemp(InputStream oriIn, int x, int y, int toX, int toY)
      throws Exception {

    String pk = DateFormatUtils.format(System.currentTimeMillis(), "S")
        + RandomStringUtils.randomAlphanumeric(2);
    ImageSaveBean isBean = ImageUtils.genImageSaveBean(EImgSaveType.TEMP, pk, ".jpg");
    checkDirs(isBean.getFullPath());

    FileOutputStream fout = null;
    try {
      IMOperation op = new IMOperation();
      op.addImage("-");
      op.quality(85d);

      BufferedImage image = ImageIO.read(oriIn);
      int imageWidth = image.getWidth();
      int imageHeight = image.getHeight();

      if (imageWidth < toX || imageHeight < toY) {
        throw new IllegalArgumentException("image width : " + imageWidth
            + ", image height : " + imageHeight);
      }
      // write image to file
      op.crop(toX, toY, x, y);
      op.addImage("jpg:-");
      fout = new FileOutputStream(new File(isBean.getFullPath()));
      ConvertCmd convert = new ConvertCmd();
      Pipe pipeIn = new Pipe(oriIn, null);
      Pipe pipeOut = new Pipe(null, fout);
      convert.setInputProvider(pipeIn);
      convert.setOutputConsumer(pipeOut);
      convert.run(op);
    } catch (InterruptedException e) {
      log.error(e.getMessage(), e);
    } catch (IM4JavaException e) {
      log.error(e.getMessage(), e);
    } finally {
      IOUtils.closeQuietly(oriIn);
      IOUtils.closeQuietly(fout);
    }

    return new File(isBean.getFullPath());
  }

  /**
   * 圖片可以先存一張 temp 檔, 透過下面的程式可以省略很多圖的問題(ex: 淘寶)
   * 
   * @param in 
   * @return
   * @throws IOException
   */
  public static File saveTempFile(InputStream in) throws IOException {

    String pk = DateFormatUtils.format(System.currentTimeMillis(), "S")
        + RandomStringUtils.randomAlphanumeric(2);
    ImageSaveBean isBean = ImageUtils.genImageSaveBean(EImgSaveType.TEMP, pk,
        ".jpg");
    checkDirs(isBean.getFullPath());
    String tempFile = isBean.getFullPath();
    FileOutputStream fout = null;
    try {

      IMOperation op = new IMOperation();
      op.addImage("-");
      op.quality(95d);
      op.addImage("jpg:-");

      fout = new FileOutputStream(new File(tempFile));
      ConvertCmd convert = new ConvertCmd();
      Pipe pipeIn = new Pipe(in, null);
      Pipe pipeOut = new Pipe(null, fout);
      convert.setInputProvider(pipeIn);
      convert.setOutputConsumer(pipeOut);
      convert.run(op);
    } catch (InterruptedException e) {
      log.error(e.getMessage(), e);
    } catch (IM4JavaException e) {
      log.error(e.getMessage(), e);
    } finally {
      IOUtils.closeQuietly(in);
      IOUtils.closeQuietly(fout);
    }
    return new File(tempFile);
  }

  /**
   * 暫存檔案
   * 
   * @param file
   * @return
   * @throws IOException
   */
  public static File saveTempFile(File file) throws IOException {
    return saveTempFile(FileUtils.openInputStream(file));
  }


  /**
   * 自動產生存圖的訊息
   * 
   * @param imgSaveType
   * @param entityPK
   *          可以寫 null
   * @param extension
   * @return
   */
  public static ImageSaveBean genImageSaveBean(EImgSaveType imgSaveType,
      String entityPK, String extension) {
    return ...
  }

  /**
   * 依 type 把圖的 http 路徑 gen 出來
   * 
   * @param enumImage
   * @param oriStr
   * @return
   */
  public static String getCurrentImgPath(EImgSaveType enumImage, String oriStr) {
    if (StringUtils.isBlank(oriStr)) {
      return null;
    }
    return getCurrentImgPath(enumImage, oriStr, -1);
  }

  /**
   * 依 type 把圖的 http 路徑 gen 出來
   * 
   * @param enumImage
   * @param oriStr
   * @return
   */
  public static String getCurrentImgPath(EImgSaveType enumImage, String oriStr,
      int width) {
    
    return ...
  }

  /**
   * 判斷目錄在不在, 不在就建立
   * 
   * @param path
   */
  public static void checkDirs(String path) {
    File containerDir = new File(path);
    if (!containerDir.getParentFile().isDirectory()) {
      containerDir.getParentFile().mkdirs();
    }
  }

 大概是這個樣子, 速度上挺快的, 又能解決一些格式上的問題, 如淘寶的圖抓下來後, 只要一透過 ImageIO.read 就會多出一層透明層的鳥問題...等.

im4java 就是把 imageMagick 給包起來, 像我們自己寫 Runtime.getRuntime().exec(commandText); 一樣, 可以把 op 印出來看看 System.out.println(op);
當然還有很多很好用的功能, 像浮水印, 可以把自己家的 url 給印上去...等.

  IMOperation op = new IMOperation();
  ....  
  // gravity : southeast 右下角, 
  op.font("Arial").gravity("southeast").pointsize(18).fill("#BCBFC8").draw("text 5,5 www.我是小胖.com");
      ...

2012年8月30日 星期四

Java-image 高解析縮圖

很多時候會用到縮圖的功能, 自己寫, 說真的我還真不會, 找了很多 library, 這套算是最好用的, 畫質也很好. java-image-scaling 有興趣的人可以下載回來玩看看, 這裡也順便分享一下自己寫的一些程式.
程式中, 存檔時只支援 .jpg or .jpeg.
另外, 有其它需求再自己改一下囉, 基本上支援, 依寬,高, 或最長的寬或高去縮圖.
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Locale;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;

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

import com.mortennobel.imagescaling.AdvancedResizeOp;
import com.mortennobel.imagescaling.ResampleOp;

/**
 * image 的工具, 主要提供縮放的功能
 * 
 * @author MarkYeh
 * 
 */
public class ImageUtil {

  private static final Log log = LogFactory.getLog(ImageUtil.class);
  
  /**
   * 判斷原來圖片的寬,有沒有比要縮的寬度還寬
   * 
   * @param srcImage
   * @param width
   * @return
   * @throws IOException
   */
  public boolean checkResizeByWidth(String srcImage, int width)
      throws IOException {
    return checkResizeByWidth(new File(srcImage), width);
  }

  public boolean checkResizeByWidth(File srcImageFile, int width)
      throws IOException {
    BufferedImage bf = ImageIO.read(srcImageFile);
    int oriWidth = bf.getWidth();
    if (oriWidth <= width) {
      return false;
    }
    return true;
  }
  
  /**
   * 判斷原來圖片的高,有沒有比要縮的高度還高
   * 
   * @param srcImage
   * @param width
   * @return
   * @throws IOException
   */
  public boolean checkResizeByHeight(String srcImage, int height)
      throws IOException {
    return checkResizeByHeight(new File(srcImage), height);
  }

  public boolean checkResizeByHeight(File srcImageFile, int height)
      throws IOException {
    BufferedImage bf = ImageIO.read(srcImageFile);
    int oriHeight = bf.getHeight();
    if (oriHeight <= height) {
      return false;
    }
    return true;
  }

  /**
   * 將最長寬或高縮放成 width的設定
   * 
   * @param imgName
   * @param type
   * @param width
   * @return
   */
  public static void resizeImage(String imgName, int width, String toFileName)
      throws IOException {
    try {
      resizeImage(ImageIO.read(new File(imgName)), width, toFileName);
    } catch (IOException e) {
      log.error(e.getMessage(), e);
    }
  }

  /**
   * 將最長寬或高縮放成 width 的設定
   * 
   * @param imgName
   * @param type
   * @param width
   * @return
   * 
   */
  public static void resizeImage(BufferedImage image, int newWidth,
      String toFileName) throws IOException {

    // Original size
    int imageWidth = image.getWidth(null);
    int imageHeight = image.getHeight(null);

    if (imageWidth > imageHeight) {
      imageWidth = newWidth;
      imageHeight = (newWidth * imageHeight) / imageWidth;
    } else {
      imageWidth = (newWidth * imageWidth) / imageHeight;
      imageHeight = newWidth;
    }

    reSizeAndSave(image, imageWidth, imageHeight, toFileName);
  }

  public static void resizeImageWithNewWidth(URL url, int newWidth,
      String toFileName) {
    try {
      resizeImageWithNewWidth(ImageIO.read(url), newWidth, toFileName);
    } catch (IOException e) {
      log.error(e.getMessage(), e);
    }
  }

  public static void resizeImageWithNewWidth(String imgName, int newWidth,
      String toFileName) {
    try {
      resizeImageWithNewWidth(ImageIO.read(new File(imgName)), newWidth, toFileName);
    } catch (IOException e) {
      log.error(e.getMessage(), e);
    }
  }

  public static void resizeImageWithNewWidth(BufferedImage image, int newWidth,
      String toFileName) throws IOException {
    resizeImageWithNewWidth(image, newWidth, toFileName, false);
  }

  public static void resizeImageWithNewWidth(BufferedImage image, int newWidth,
      String toFileName, boolean checkWidth) throws IOException {

    // Original size
    int imageWidth = image.getWidth(null);
    int imageHeight = image.getHeight(null);

    if (checkWidth && imageWidth < newWidth) {
      reSizeAndSave(image, imageWidth, (imageWidth * imageHeight) / imageWidth,
          toFileName);
    } else {
      reSizeAndSave(image, newWidth, (newWidth * imageHeight) / imageWidth,
          toFileName);
    }
  }

  /**
   * 依高縮放
   * 
   * @param image
   * @param imageUtilType
   * @param maxWidth
   * @return
   */
  public static void resizeImageWithNewHeight(String imgName, int newHeight,
      String toFileName) throws IOException {
    try {
      resizeImageWithNewHeight(ImageIO.read(new File(imgName)), newHeight,
          toFileName);
    } catch (IOException e) {
      log.error(e.getMessage(), e);
    }
  }

  public static void resizeImageWithNewHeight(BufferedImage image,
      int newHeight, String toFileName) throws IOException {

    // Original size
    int imageWidth = image.getWidth(null);
    int imageHeight = image.getHeight(null);

    reSizeAndSave(image, (newHeight * imageWidth) / imageHeight, newHeight,
        toFileName);
  }

  /**
   * 縮放與存檔, gif and png 不能壓縮, jpg 則壓縮後存檔
   * 
   * @param source
   * @param width
   * @param height
   * @param toFileName
   * @throws IOException
   */
  private static void reSizeAndSave(BufferedImage source, int width,
      int height, String toFileName) throws IOException {
    
    if (!toFileName.toLowerCase().matches("(.*\\.jpe?g)")) {
      throw new IllegalArgumentException("存儲的附檔名只能是 jpg 或 jpeg : " + toFileName);
    }
    
    ResampleOp resampleOp = new ResampleOp(width, height);
    resampleOp.setUnsharpenMask(AdvancedResizeOp.UnsharpenMask.Normal);
    BufferedImage dest = resampleOp.filter(source, null);

    // 檢查圖檔儲存的資料夾是否已存在, 不存在則建立資料夾
    String path = toFileName;
    
    File containerDir = new File(path);
    if (!containerDir.getParentFile().isDirectory()) {
      containerDir.getParentFile().mkdirs();
    }

    if (source.getType() == BufferedImage.TYPE_CUSTOM) { // png
      String format = "png";
      ImageIO.write(dest, format, new File(toFileName));
    } else if (source.getType() == BufferedImage.TYPE_BYTE_INDEXED) { // gif
      String format = "png";
      ImageIO.write(dest, format, new File(toFileName));
    } else {
      ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
      ImageOutputStream ios = ImageIO.createImageOutputStream(new File(toFileName));
      writer.setOutput(ios);
      ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());
      iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
      iwparam.setCompressionQuality(0.95F);
      try {
        writer.write(null, new IIOImage(dest, null, null), iwparam);
        ios.flush();
        writer.abort();
        writer.dispose();
      } finally {
        if (ios != null) {
          ios.close();
        }
        if (writer != null) {
        }
      }
    }
  }

  public static void main(String[] args) throws Exception {
    ImageUtil.resizeImage("D:\\222.jpg", 200, "D:\\200_wh.jpg");
    ImageUtil.resizeImageWithNewWidth("D:\\222.jpg", 200, "D:\\aa\\200_w.jpg");
    ImageUtil.resizeImageWithNewHeight("D:\\222.jpg", 200, "D:\\aa\\200_h.jpg");
    System.out.println("done");
  }

}

2012年8月29日 星期三

Java-Image 取得附檔名

ImageInputStream iis = ImageIO.createImageInputStream(new FileInputStream(picFile));
Iterator readerIterator = ImageIO.getImageReaders(iis);
if (readerIterator.hasNext()) {
    ImageReader reader = readerIterator.next();
    System.out.println(reader.getFormatName());
}