2012년 10월 25일 목요일

java Thumbnail

오랜만에 포스팅...
Thumbnail image 만들기를 하면서 구글링결과 그대로 쓰기는 무리가 있었고,
역시 짜집기 국가대표급 능력으로 여러 소스를 분석하여 드디어 완성.

필요한 기능은 간단.
1. 원본 파일
2, 저장될 파일
3. 넓이

3이 가장 중요하다.
이유는 간단. Thumbnail image의 원본에 비율이 다르다는것.

그래서 결국 저장될 이미지의 최대치만 지정하면 원본 이미지의 비율에 맞춰 높이를 자동으로 저장한다는 것.

그 소스는....

Image inImage = new ImageIcon(loadFile).getImage();
double scale = (double) maxDim / (double) inImage.getHeight(null);
if (inImage.getWidth(null) > inImage.getHeight(null)) {
  scale = (double) maxDim / (double) inImage.getWidth(null);
}
int scaledW = (int) (scale * inImage.getWidth(null));
int scaledH = (int) (scale * inImage.getHeight(null));

loadFile - 원본 이미지 파일 전체경로 및 파일명(ex: c:\file\test.jpg)
scaledW - 수정된 넓이
scaledH  - 수정된 높이

파일을 저장하는 소스도 문제가 있었다.
문제의 소스

JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os);
encoder.encode(outImage);


표준이 아니어서 jdk버전을 탄다는것...ㅡㅡ;;;

그래서 완성된 소스는...

public boolean createThumbnail(String loadFile, String saveFile, int maxDim)
   throws IOException {
  File save = new File(saveFile.replaceAll("/", "\\" + File.separator));
  FileInputStream fis = new FileInputStream(loadFile.replaceAll("/", "\\"
    + File.separator));
  BufferedImage im = ImageIO.read(fis);

  Image inImage = new ImageIcon(loadFile).getImage();
  double scale = (double) maxDim / (double) inImage.getHeight(null);
  if (inImage.getWidth(null) > inImage.getHeight(null)) {
   scale = (double) maxDim / (double) inImage.getWidth(null);
  }

  int scaledW = (int) (scale * inImage.getWidth(null));
  int scaledH = (int) (scale * inImage.getHeight(null));

  BufferedImage thumb = new BufferedImage(scaledW, scaledH,
    BufferedImage.TYPE_INT_RGB);
  Graphics2D g2 = thumb.createGraphics();

  g2.drawImage(im, 0, 0, scaledW, scaledH, null);
  return ImageIO.write(thumb, "jpg", save);
 }


loadFile - 원본 이미지 파일 전체경로 및 파일명(ex: c:\file\test.jpg)
saveFile - 저장될 이미지 파일 전체경로 및 파일명(ex: c:\file\test_300.jpg)
maxDim - 저장될 이미지의 최대 pixel(ex:300)

여기서 다 알겠지만 간단한 팁은.
만약 넓이와 높이가 지정되는 경우라면 scaledW, scaledH 를 계산하지 않고 직접 지정하면 된다는거다.

java Thumbnail 소스를 찾다거 맨붕 오신 분들에게 한방울의 생명수가 되길 바라며.

블로그 통계를 보니 외쿡에서도 간혹 들어온다.
그래서 의도적으로 ex를...ㅋㅋㅋ

#SCMInno #에스씨엠이노 #WEBDeK

2011년 1월 12일 수요일

javascript Map Json

원리대로 JSON을 만드는
{"id":"atspeed","name":"송성일"}
이런식은 교범에나 나오는 방법으로 실무에서는 그닥 사용 빈도가 높지 않다.

보통은 <script type="text/javascript" src="json2.js"></script>을 이용해서

function User(id, name) {
this.id = id;
this.name = name;
}

function userJsonString(){
var user = User("atspeed","송성일");
var jsonString = JSON.stringify(user);
return jsonString;
}

Json String으로 변환을 하기 위해 먼저 만든 Map을 그냥 사용한다면 아래와 같은 문제가 발생한다.
var map = new Map();
  map.put("t1", "test1");
  map.put("t2", "test2");
  map.put("t3", "test3");
  map.put("t4", "test4");
  map.put("t5", "test5");

  var map1 = new Map();
  map1.put("m1", "map1");
  map.put("m1", map1);

  var map2 = new Map();
  map2.put("m2", "map2");
  var map3 = new Map();
  map3.put("m3", "map3");
  var list = new Array();
  list[0] = map2;
  list[1] = map3;

  map.put("list", list);

map.jsonString() : {"t1":"test1","t2":"test2","t3":"test3","t4":"test4","t5":"test5","m1":{"map":{"m1":"map1"}},"list":[{"map":{"m2":"map2"}},{"map":{"m3":"map3"}}]}

즉, Map객체의 this.map이 m1의 아래 나온다.
"m1":{"map":{"m1":"map1"}}
우리가 원하는 결과는"m1":{"m1":"map1"} 이거다.

이 문제를 해결하기 위해 예전에 사용하던 방법은
 typeof this.map[prop] 를 이용해 타입별로 jsonString을 생성하는 거였다.

하지만 좀더 쉬운 방법으로
putMap : function(key, value){
     this.map[key] = value.map;
    },
함수를 추가하는 거다.

위의 예제에서 map.put("m1", map1); 부분을 map.putMap("m1", map1); 으로 바꾸면
map.jsonString() : {"t1":"test1","t2":"test2","t3":"test3","t4":"test4","t5":"test5","m1":{"m1":"map1"},"list":[{"map":{"m2":"map2"}},{"map":{"m3":"map3"}}]}
원하는 결과가 나온다.

list또한 조금 문제가 될수 있지만 스팩만 이해 한다면 사용하는데는 문제가 없다.
그래도 바꾸고자 한다면,
putMapList : function(key, value){
     var list = new Array();
     for(var i=0;i<value.length;i++){
      list.push(value[i].map);
     }
     this.map[key] = list;
    },
를 추가한다.

사용법은 map.putMapList("list", list);
map.jsonString() : {"t1":"test1","t2":"test2","t3":"test3","t4":"test4","t5":"test5","m1":{"m1":"map1"},"list":[{"m2":"map2"},{"m3":"map3"}]}
깔끔한 jsonString을 사용할수 있다.

결과적으로
Map = function(){
 this.map = new Object();
};  
Map.prototype = {  
    put : function(key, value){  
        this.map[key] = value;
    },
    putMap : function(key, value){
     this.map[key] = value.map;
    },
    putMapList : function(key, value){
     var list = new Array();
     for(var i=0;i<value.length;i++){
      list.push(value[i].map);
     }
     this.map[key] = list;
    },
    get : function(key){  
        return this.map[key];
    },
    containsKey : function(key){   
     return key in this.map;
    },
    containsValue : function(value){   
     for(var prop in this.map){
      if(this.map[prop] == value) return true;
     }
     return false;
    },
    isEmpty : function(key){   
     return (this.size() == 0);
    },
    clear : function(){  
     for(var prop in this.map){
      delete this.map[prop];
     }
    },
    remove : function(key){   
     delete this.map[key];
    },
    keys : function(){
        var keys = new Array();
        for(var prop in this.map){
            keys.push(prop);
        }
        return keys;
    },
    values : function(){  
     var values = new Array();  
        for(var prop in this.map){  
         values.push(this.map[prop]);
        }  
        return values;
    },
   size : function() {
    var count = 0;
    for (var prop in this.map) {
      count++;
    }
    return count;
   },
    jsonString: function(){
     return JSON.stringify(this.map);   
    }
};
사용법은
function userJsonString(){
 var map= new Map();
 map.put("id", "atspeed");
 map.put("name", "송성일");
 return map.jsonString();
}

ajax를 태울때는 그냥
function sendUser(){
 var map= new Map();
 map.put("id", "atspeed");
 map.put("name", "송성일");
 sendJsonAjax("user.json", map, "userResult");
}

function userResult(json){
 ...
}

var SEND_JSON_TIMEOUT = 5000;
function sendJsonAjax(sendUrl, map, successFunction, failFunction){
 $.ajax({
  type: "post",
  url: sendUrl,
  data: map.jsonString(),
  dataType: "json",
  timeout: SEND_JSON_TIMEOUT,
  error: function(a, b, c){        
         if(failFunction != null){
          eval(failFunction+"(a,b,c)");
         }else{
          alert('오류가 발생했습니다. 다시 시도해 주십시요.'+ a +' / ' + b +' / ' + c);
         }
     },
     success: function (json) {
      if(successFunction != null){
       eval(successFunction+"(json)");
      }
     }
 });
}

물론 Map에 다른 Map이나 Array를 담아도 문제가 없다.

var map = new Map();
  map.put("t1", "test1");
  map.put("t2", "test2");
  map.put("t3", "test3");
  map.put("t4", "test4");
  map.put("t5", "test5");

  var map1 = new Map();
  map1.put("m1", "map1");
  map.putMap("m1", map1);

  var map2 = new Map();
  map2.put("m2", "map2");
  var map3 = new Map();
  map3.put("m3", "map3");
  var list = new Array();
  list[0] = map2;
  list[1] = map3;

  map.putMapList("list", list);

map.jsonString() : {"t1":"test1","t2":"test2","t3":"test3","t4":"test4","t5":"test5","m1":{"m1":"map1"},"list":[{"m2":"map2"},{"m3":"map3"}]}

그냥 일반적인 jQuery를 이용한 ajax사용법이나, callBack받은 json데이터의 사용법은 생략한다.

일반적인 내용은 나 말고도 더 정리 잘하는 분들이 계시니 그분들의 글을 활용하시면 될듯.
하지만 필자가 구글링을 아무리 해도 정리된 내용의 이해가 어려운 경우는 내가 이해한 방식으로 글을 올리도록 해야겠죠?

#SCMInno #에스씨엠이노 #WEBDeK

javascript Map HashMap

요즘 javascript의 대세는 역시 jQuery일것이다.
jQuery를 사용해 보니 가볍고 편리한게 장점인듯하다.

그래도 노가다 코딩을 워낙 싫어라 하는 탓에 jQuery를 한꺼플씩 포장해서 사용하고 있다.

제목과는 전혀 맞지 않는 서두라 죄송...^^

아무튼 구글링을 하면 관련 자료들이 많이 나오지만 사용중인 코드는 이거다.

Map = function(){
 this.map = new Object();
};  
Map.prototype = {  
    put : function(key, value){  
        this.map[key] = value;
    },  
    get : function(key){  
        return this.map[key];
    },
    containsKey : function(key){   
     return key in this.map;
    },
    containsValue : function(value){   
     for(var prop in this.map){
      if(this.map[prop] == value) return true;
     }
     return false;
    },
    isEmpty : function(key){   
     return (this.size() == 0);
    },
    clear : function(){  
     for(var prop in this.map){
      delete this.map[prop];
     }
    },
    remove : function(key){   
     delete this.map[key];
    },
    keys : function(){  
        var keys = new Array();  
        for(var prop in this.map){  
            keys.push(prop);
        }  
        return keys;
    },
    values : function(){  
     var values = new Array();  
        for(var prop in this.map){  
         values.push(this.map[prop]);
        }  
        return values;
    },
    size : function(){
      var count = 0;
      for (var prop in this.map) {
        count++;
      }
      return count;
    },
    toString : function(){
      var s=[];
      for(var prop in this.map){
         s.push(prop+':'+this.map[prop]);
      }
      return s.join(',');
    }
};

사용법은 간단하다.
var map = new Map();
 map.put("user_id", "atspeed");
 -----------------------
map.get("user_id");

그냥 java Map과 동일하게 사용하면 되는거다.

#SCMInno #에스씨엠이노 #WEBDeK

2011년 1월 4일 화요일

ibatis JSON 연동

프레임워크를 사용하다보면 가장 큰 문제는 데이터 옮겨담기다.

예를 들어 ibatis에 리턴한 형태가 Map인데 이걸 다시 bean에 옮겨 담아야 한다면 불필요한 리소스를 사용하게 될것이다.

JSON방식으로 Ajax를 구현하면서 발생한 문제가 이거다.

실제로 ibatis에서 결과를 JSONObject형태로 받을 수 없기 때문이다.
resultClass로 JSONObject를 선언한다 하더라고 getter, settger가 없는 일반 Object이기 때문에 오류가 발생한다.
그렇다고 JSONObject가 Map을 implement하거나 HashMap을 상속받은 구조도 아니다.
결국 직접 사용하기 위해 만들어야 했다.

package com.atspeed;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.json.JSONObject;
public class JSONMap extends JSONObject implements Map{
 @Override
 public int size() {
  // TODO Auto-generated method stub
  return super.length();
 }
 @Override
 public boolean isEmpty() {
  // TODO Auto-generated method stub
  return (super.length()==0?true:false);
 }
 @Override
 public boolean containsKey(Object key) {
  // TODO Auto-generated method stub
  return !super.isNull(key.toString());
 }
 @Override
 public boolean containsValue(Object value) {
  // TODO Auto-generated method stub
  Iterator it = super.keys();
  while(it.hasNext()){
   String key = it.next().toString();
   try {
    if(super.get(key) == value){
     return true;
    }
   } catch (Exception e) {
    // TODO: handle exception
   } 
  }
  return false;
 }
 @Override
 public Object get(Object key) {
  // TODO Auto-generated method stub
  try {
   return super.get(key.toString());
  } catch (Exception e) {
   // TODO: handle exception
  }
  return null;
 }
 @Override
 public Object put(Object key, Object value) {
  // TODO Auto-generated method stub
  try {
   return super.put(key.toString(), value);
  } catch (Exception e) {
   // TODO: handle exception
  }
  return null;
 }
 @Override
 public Object remove(Object key) {
  // TODO Auto-generated method stub
  return super.remove(key.toString());
 }
 @Override
 public void putAll(Map m) {
  // TODO Auto-generated method stub  ;
  Iterator it = m.keySet().iterator();
  while(it.hasNext()){
   String key = it.next().toString();
   try {
    super.put(key, m.get(key));
   } catch (Exception e) {
    // TODO: handle exception
   } 
  }
 }
 @Override
 public void clear() {
  // TODO Auto-generated method stub
  Iterator it = super.keys();
  while(it.hasNext()){
   String key = it.next().toString();
   super.remove(key);
  }
 }
 @Override
 public Set keySet() {
  // TODO Auto-generated method stub
  return null;
 }
 @Override
 public Collection values() {
  // TODO Auto-generated method stub
  return null;
 }
 @Override
 public Set entrySet() {
  // TODO Auto-generated method stub
  return null;
 }
}
사실 구현해야할 몇가지 Map관련 함수는 빼먹었다.

이걸 사용하는 방법은

SqlMap.xml쪽에서
<typeAlias alias="JSONMap" type="com.atspeed.JSONMap"/>
 <select id="json_list" parameterClass="Map" resultClass="JSONMap">

이것만 해주면 된다.
결론적으로 ibatis에서Map으로 인정받고 사용하는거다.
결국 Service나 Control단에서는 결과는 JSONMap으로 꺼내 사용하면되는것이고.
실제 JSONObject를 상속받았기 때문에 사용법은 JSONObject와 동일하다.

#SCMInno #에스씨엠이노 #WEBDeK

android ListView CheckBox onListItemClick

영어 실력이 안드로메다인지라 제목에 그냥 키워드를 나열했군요...ㅡㅡ;;;

이 글의 진정한 제목은
CheckBox를 포함한 ListView의 onListItemClick 발생시키기다.

ListView의 Item에 CheckBox가 포함된 경우 onListItemClick이벤트가 발생하지 않는다.
그건 아마도 CheckBox가 이벤트를 잡수시는 엄청난 포스가 있으신듯하다.

그리하여 결국 내가 선택한 방법은 CheckBox를 클릭할때 ListView의 onListItemClick를 강제로 발생시키는 방법이다.
머 이런 경우는 웹에서도 종종 발생하는 경우이기도 하다.
여러 컴퍼넌트에서 이벤트가 발생하면 뜻하지 않은 이상한 결과가 발생하는건 당연한듯하다.

결론은(클래스명도 안드로메다임을 알아주시길...)

import android.view.View;
import android.widget.ListView;
public class ListInCheckItemClickAdapter implements View.OnClickListener {
 ListView listView;
 int position;
 int id;
 public ListInCheckItemClickAdapter(ListView listView, int position, int id){
  this.listView = listView;
  this.position = position;
  this.id = id;
 }

 @Override
 public void onClick(View v) {
  // TODO Auto-generated method stub
  listView.performItemClick(v, position, id);
 }
}

여기서 핵심은
listView.performItemClick(v, position, id);
이벤트를 강재로 발생시키는 함수는 perform으로 시작한다.

그렇다면 만들어논 클래스를 사용하는 방법은
ListView에 연결된 BaseAdapter의 getView에서 Item생성시 CheckBox에 clickEvent로 이 객체를 연결하는 방식이다.

checkBox.setOnClickListener(new ListInCheckItemClickAdapter(getListView(), position, checkBox.getId())); 사용법은 간단하다.
getListView()는 아래 글중에 한번 언급한듯 하다.
ListActivity를 상속받은 경우 ListView를 가져오는 함수다.

참고로 BaseAdapter는 inner class로 구현했다.

사실 BaseAdapter도 자주 쓰는 부분 getCount, getItem, getItemId, getView등의 중복코딩이 있어 별도로 구현한 Adapter를 사용한다.
내 경우는 반복코딩이나 무한 삽질을 워낙 싫어하다 보니 구현해서 사용하지만 개발자마다 취향이 있어 포스팅까지는 필요없다고 생각한다.

#SCMInno #에스씨엠이노 #WEBDeK

2010년 12월 30일 목요일

android post send

앞서 작성한 글에서 언급한 android에서의 기본적인 POST방식을 언급하지 않으려 했으나...
아무튼 적어보련다.

public HttpResponse sendData(String url, String encoding, Map param){
  HttpResponse response = null;
  try {
   HttpClient httpClient = new DefaultHttpClient();
   HttpPost httpPost = new HttpPost(url);
   List list = new ArrayList();
   Iterator it = param.keySet().iterator();
   while(it.hasNext()){
    String key = (String)it.next();
    list.add(new BasicNameValuePair(key,param.get(key).toString()));
   }
   httpPost.setEntity(new UrlEncodedFormEntity(list, encoding));
   response = httpClient.execute(httpPost);
  } catch (Exception e) {
   Log.e("Error", "sendData", e);
  }

  return response;
 }

앞선 2개의 글을 보신 분이라면 나름 내 스타일을 아실터...ㅋㅋ

사실 너무 간단한 소스라 별로 설명할건 없다.

추가로 response는 어찌할것인가.

public String getResponseContent(HttpResponse response){
  StringBuilder sb = new StringBuilder();
  try {
   if(response!=null){
             BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
    String str;
    while((str = br.readLine()) != null){
     sb.append(str).append("\n");
    }
   }
  } catch (Exception e) {
   Log.e("Error", "getResponseContent", e);
  }

  return sb.toString();
 }

response에서 내용을 추출하는 코드다.
내가 이렇게 사용한 이유는 이넘을 다시 JSONArray로 변환하기 위해서다.

만약 일반적인 parameter를 추출하기 원한다면
HttpEntity resEntity = response.getEntity();
이걸 이용해서 send하면서 담을때 처럼 거꾸로 사용하면 될듯하다.
해보지 않아서 어찌 할지는 정확하게 모르겠다.
하지만 일반적으로 XML, JSON을 사용한다면 String만 추출하면되니 문제는 없을듯하다.

#SCMInno #에스씨엠이노 #WEBDeK

android file upload

android에서 파일을 전송하려면 코드는 간단하다.

public String sendFile(String urlServer, String fileName){
  File file = new File(fileName);
  String result = null;
  try {
       HttpClient client = new DefaultHttpClient();
       HttpPost post = new HttpPost(urlServer);
       ContentBody bin = new FileBody(file);
       MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); 
       reqEntity.addPart("file", bin);
       post.setEntity(reqEntity); 
       HttpResponse response = client.execute(post); 
       /*
       HttpEntity resEntity = response.getEntity(); 
       if (resEntity != null) {   
        Log.w("RESPONSE",EntityUtils.toString(resEntity));
       }
       */
       if (response != null){
        result = getResponseContent(response);
       }
  } catch (Exception e) {
   Log.e("Error", "sendFile", e);
  }
  return result;
 }

일반적인 POST방식의 전송과 기본을 동일하다.
POST에 담을 entity를
ContentBody bin = new FileBody(file);
       MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); 
       reqEntity.addPart("file", bin);
선언한다는게 특이한 점이다.

물론 일반적인 parameter를 추가하고 싶다면 일반적인 POST방식처럼 add하면 된다.
하지만 이 부분에서의 삽질은 사용하려는 class가 android가 지원하는 class가 아니라는점.

httpclient-4.0.3.jar
httpmime-4.0.3.jar
apache-mime4j-0.6.jar

buildpath에 추가해야 한다.
프로젝트 바로 하위에 lib폴더를 만들고 추가했다.
구글링을 해보면
httpclient-4.0.3.jar
httpmime-4.0.3.jar
이것만 필요하다고 나오지만
apache-mime4j-0.6.jar
이게 없으면 절대 않된다.

return받은 response를 활용하는 부분은 기본적인 POST전송방식과 동일하다.

#SCMInno #에스씨엠이노 #WEBDeK