2015. 4. 24. 16:55

public String getMcodeFromMLB() {
HttpURLConnection uc = null; // 연결용 커넥션
URL url = null;
String sUrl = "http://localhost:8080/MLBServerTest/MLBTest.jsp"; // 연결할 주소
String result = "";

try {

url = new URL(sUrl);

uc = (HttpURLConnection) url.openConnection();

uc.setDoInput(true);
uc.setDoOutput(true);
uc.setUseCaches(false);
uc.setRequestMethod("POST");
uc.setConnectTimeout(10000); // 커넥션 타임아웃
uc.setAllowUserInteraction(true);

// Http Header Setting
uc.setRequestProperty("Content-type", "application/x-www-form-urlencoded;charset=euc-kr");

// Http Parameter Sending
String partner_id = "O00128"; // CP의 파트너ID
String service_code = "001"; // 각 CP서버에 할당된 코드
String music_code = "00293876"; // MLB에서 조회된 MUSIC_CODE
String content_price = "800"; // 컨텐츠 단가
String content_name = "핑계"; // 컨텐츠가 서비스되는 이름
String content_num = "1"; // 패키지에 포함한 컨텐츠의 갯수. 단일상품일 경우 1, 복합상품일 경우 2이상
String pid = "3102306023"; // PID
String sub_code = "12700000012"; // CP서버가 자체적으로 관리하는 Code(SCID등). 12자리 이하
String carrier_code = "00"; // ASP에 서비스하는 캐리어를 구분하기 위한 구분코드

StringBuffer sb = new StringBuffer();
sb.append(MLBConstants.MCODE_REQUEST_PARTNER_ID).append("=").append(partner_id).append("&");
sb.append(MLBConstants.MCODE_REQUEST_SERVICE_CODE).append("=").append(service_code).append("&");
sb.append(MLBConstants.MCODE_REQUEST_MUSIC_CODE).append("=").append(music_code).append("&");
sb.append(MLBConstants.MCODE_REQUEST_CONTENT_PRICE).append("=").append(content_price).append("&");
sb.append(MLBConstants.MCODE_REQUEST_CONTENT_NAME).append("=").append(content_name).append("&");
sb.append(MLBConstants.MCODE_REQUEST_CONTENT_NUM).append("=").append(content_num).append("&");
sb.append(MLBConstants.MCODE_REQUEST_PID).append("=").append(pid).append("&");
sb.append(MLBConstants.MCODE_REQUEST_SUB_CODE).append("=").append(sub_code).append("&");
sb.append(MLBConstants.MCODE_REQUEST_CARRIER_CODE).append("=").append(carrier_code);

PrintWriter pw = new PrintWriter(new OutputStreamWriter(uc.getOutputStream(), "euc-kr"));
pw.write(sb.toString());
pw.flush();


int resCode = 0; // RMS 와의 연결 응답값
resCode = uc.getResponseCode();

StringBuffer resp = new StringBuffer();
if(resCode < 400){ // 연결이 성공적일때

String line;
BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream(), "euc-kr"));
while ((line = br.readLine()) != null) {
System.out.println(line);
resp.append(line);
}

pw.close();
br.close();

// html 파싱
result = getResultCode(resp.toString());
result = (result.equals("OK")) ? result : "MLB연동 중 에러 발생 : " + getResultCode(resp.toString());
}
else{
result = "MLB연동 중 에러 발생 : " + resCode + " 에러입니다.";
}

} catch (IOException e) {
e.printStackTrace();
}
catch(Exception e) {
e.printStackTrace();
}

return result;
}

넘기고 받을 때 euc-kr로 변환하는 것도 주목.

http://lonelycat.tistory.com/314

'ApplicationPrograming > Java' 카테고리의 다른 글

자바 암호화  (0) 2015.02.25
Commons-Fileupload의 한글관련 문제 추가  (0) 2013.03.15
Commons-DbUtils  (0) 2013.03.15
Commons-Fileupload의 한글관련 문제 추가  (0) 2013.03.15
JDOM  (0) 2013.01.31
Posted by 물색없는세상
2015. 4. 23. 18:14

HttpURLConnection을 이용해서 통신프로그램을 작성하던중... POST방식을 네이버 검색해보니 일부 블로그에서
파라미터를 아래와 같은 방식 처리하는 예제가 있었다.

String param = "xx="+xx+"&xxx="+xxx+"&xxxx="+xxxx;

URL targetURL = new URL(http://xxx.xxx.xxx.xxx/xxx/xxx.html);


... 중략 ...

PrintWriter out =

new PrintWriter(hurlc.getOutputStream());

out.println(param);

out.flush();

out.close();


해본 결과 PrintWriter 쓰면 안된다. (이것 때문에 또 삽질을....역시 네이버는 너무 믿으면 안된다 ㅡ_ㅡ;;;)
구글링을 해본결과 아래와 같이 OutputStream을 써야한다.

String param = "xx="+xx+"&xxx="+xxx+"&xxxx="+xxxx;

URL targetURL = new URL(http://xxx.xxx.xxx.xxx/xxx/xxx.xxx);

URLConnection urlConn = targetURL.openConnection();

HttpURLConnection hurlc = (HttpURLConnection) urlConn;

// 헤더값을 설정한다.

hurlc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// 전달 방식을 설정한다. POST or GET, 기본값은 GET 이다.
hurlc.setRequestMethod(
"POST");
// 서버로 데이터를 전송할 수 있도록 한다. GET방식이면 사용될 일이 없으나, true로
// 설정하면 자동으로 POST로 설정된다. 기본값은 false이다.

hurlc.setDoOutput(
true);
// 서버로부터 메세지를 받을 수 있도록 한다. 기본값은 true이다.
hurlc.setDoInput(
true);
hurlc.setUseCaches(
false);

hurlc.setDefaultUseCaches(false);


//안됨...

//PrintWriter out = new PrintWriter(hurlc.getOutputStream());

//out.println(param);

//out.flush();

//out.close();

OutputStream opstrm = hurlc.getOutputStream();

opstrm.write(param.getBytes());

opstrm.flush();

opstrm.close();

String buffer = null;

BufferedReader in =

new BufferedReader(new InputStreamReader
(hurlc.getInputStream()));

while ((buffer = in.readLine()) != null) {

ecgResultXML += buffer;

}

in.close();

 

http://everlikemorning.tistory.com/47

Posted by 물색없는세상
2015. 4. 23. 18:14

안드로이드에서 HTTP 연결은 다음 두 가지 방법 중 하나를 통해 구현할 수 있다.

첫번째 방법은 HttpClient 클래스 혹은 이 클래스의 하위 클래스들(AbstractHttpClient, AndroidHttpClient, DefaultHttpClient)을 이용하는 방법이다. 하지만 이 클래스를 이용하는 방법은 간단하기는 성능이 다소 떨어지고 ICS 이상의 버전에서는 많은 버그를 내포하고 있어 프로요나 진저브래드 버전에서 동작하는 어플리케이션을 구현할 때 적합하다.

두번째 방법은 HttpURLConnection 클래스를 이용하는 방법이다. 이 클래스를 이용하면 처음에 구현이 다소 까다롭기는 하나 더 효율적이고 유연하고 가볍게 동작하며 ICS 이상의 버전에서도 문제없이 작동하는 어플리케이션을 만들 수 있다.

또 허니콤 이상의 버전에서 HTTP 연결을 구현하고자 할 때에는 메인 액티비티에서 구현할 경우 StrictMode$AndroidBlockGuardPolicy.onNetwork 에러가 발생한다. 따라서 별로의 스레드를 구성하여 HTTP 연결을 구현하야 한다.


이 글에서는 허니콤 버전의 환경에서 HttpURLConnection 클래스를 사용하여 스레드를 통해 HTTP 연결을 하고 POST 메세지를 웹 서버로 전송하는 방법을 다루고자 한다.

디바이스 외의 장치와 서로 상호작용하는 HTTP 네트워킹의 경우에는 항상 예측하지 못한 딜레이를 야기할 수 있다. 따라서 메인 액티비티(혹은 UI 스레드) 외에 별로의 스레드에서 HTTP 동작을 수행하는 것이 올바른 구현이다. AsyncTask 클래스는 메인 액티비티와 별도의 스레드를 구현하기 위한 간단한 방법 중 하나다.

AsyncTask는 짧은 기간 동안만 동작하는 스레드를 구현하는데 최적화되어 있다. 따라서 지금과 같이 잠시 POST를 하는 등의 동작에는 적합하지만 오랜 시간 동안 백그라운드에서 HTTP 연결을 유지하기에는 적합하지 않다.

우선 AsyncTask를 상속받는 HttpConnectionThread 라는 클래스를 메인 액비티비 클래스의 내부 클래스로 정의한다.(메인 클래스의 내부 클래스로 정의해야 UI 요소에 접근하기가 용의하다.) AsyncTask 내의 메소드 중 doInBackground() 메소드는 백그라운드에서 HTTP 연결에 실제하는 부분이 동작되고, onPostExecute() 메소드는 HTTP 웹 서버로 부터 받아온 정보 등을 UI에 전달하기 위해 사용된다.

public class HttpConnectionThread extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... url) {
// URL 연결이 구현될 부분
return null;
}
@Override
protected void onPostExecute(String result) {
// UI 업데이트가 구현될 부분
}
}

먼저 doInBackground() 내부에 HTTP 연결을 하고 POST 메세지를 전송하는 구현을 해보자. 우선 연결할 URL 주소를 파라미터로부터 받아와 URL 클래스 인스턴스를 이용하여 입력한 후에, 그 외 연결에 필요한 기본적인 설정을 해준다. 요청 방식은 “POST”로 지정하고 connect() 메소드를 호출하면 POST 메세지가 전송된다. 전송의 결과를 response라는 String 객체를 통해 전달 받은 후 반환해 주면, 이후 구현될 onPostExecute() 메소드에서 활용할 수 있다.

@Override
protected String doInBackground(String... url) {
URL url;
String response = null;
try {
url = new URL(url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.connect();
response = conn.getResponseMessage();
Log.d("RESPONSE", "The response is: " + response);
}
catch (IOException e) {
}
return response;
}

위의 doInBackground() 결과로 반환했던 String 객체가 onPostExecute() 메소드의 result라는 String 객체 파라미터로 들어온다. 이 파라미터를 이용하여 토스트 메세지를 띄워 웹 서버로부터 받은 응답 메세지를 확인할 수 있다.

@Override
protected void onPostExecute(String result) {
Toast.makeText(getApplicationContext(), result, Toast.LENGTH_SHORT).show();
}

이렇게 구현한 HttpConnectionThread 라는 이름의 내부 클래스를 메인 액티비티의 필요한 곳에서 new 생성자를 이용해 생성한 후 excute() 메소드를 통해 url을 넘겨 실행해주면 된다. 아래 예제에서는 리스트의 특정 아이템을 선택했을 때 실행되는 onListItemClick()이라는 메소드 내부에 구현하였다.

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Thread for Http connection
new HttpConnectionThread.excute("http://http://dontsdondone.esy.es/");
}

 

 

http://dontsdondone.esy.es/archives/90

 

Posted by 물색없는세상
2015. 4. 23. 18:11

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;

public class URLConnectionPOST {
public static void main(String[] args) {
try {
// Construct data
String data = URLEncoder.encode("key1", "UTF-8") + "=" + URLEncoder.encode("value1", "UTF-8");
data += "&" + URLEncoder.encode("key2", "UTF-8") + "=" + URLEncoder.encode("홍길동", "UTF-8");

// Send data
URL url = new URL("http://localhost:80/TestWeb/sample.jsp");
URLConnection conn = url.openConnection();
// If you invoke the method setDoOutput(true) on the URLConnection, it will always use the POST method.
conn.setDoOutput(true);
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
wr.write(data);
wr.flush();

// Get the response
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF-8"));
String line;
while ((line = rd.readLine()) != null) {
System.out.println(line);
}
wr.close();
rd.close();
}
catch (Exception e) {
}
}
}


 

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<%
//request.setCharacterEncoding("UTF-8");
String value1 = request.getParameter("key1");
String value2 = request.getParameter("key2");

out.println("This is server response.");
out.println("key1="+value1);
out.println("key2="+value2);
%>
</body>
</html>


URLConnectionPOST.java를 컴파일하고 위의 sample.jsp 를 포함한 Tomcat을 기동한 후에 URLConnectionPOST를 실행하면 다음과 같은 결과를 확인할 수 있다.

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
This is server response.
key1=value1
key2=홍길동
</body>
</html>

 

http://micropilot.tistory.com/290

Posted by 물색없는세상
2015. 2. 25. 15:41

자바는 친절하게도 암호화 및 메시지 검증 코드를 구현해주는 클래스를 제공해준다.
JCE(Java Cryptography Extension)란 이름의 프레임워크가 바로 그놈이다.
J2SE 1.4 이후부터는 JCE 1.2.2가 기본적으로 포함되어 있어서 별다른 라이브러리를 추가해주지 않아도 사용할 수 있다.
초기 버젼인 JCE 1.2는 미국 보안법(?)인가에 걸려서 국내에서는 사용할 수가 없었다. (무기밀매와 똑같은 처벌을 한다는 소문이..)
JCE 1.2.1이 나오면서 제한이 풀어져서, 세계적으로 많이(?) 사용하게 되었다.
그런데 이 JCE 1.2.1 버전의 자체 디지털 서명이 2005년 7월 27일쯤인가 만료가 되서, 2005년도에 파란을 한번 일으킨적도 있다.
(그럼 만료기간을 어떻게 알수 있을까? 쉽게 알려면, 자신이 사용하는 파일의 정보를 보면 된다.
자신이 사용하는 자바 관련 디렉토리에서 jce.jar를 풀어보면 META-INF에 JCE_RSA.RSA 파일이 있을것이다. 이 파일을 보면 알 수 있는데, 윈도우 환경에서 이 파일의 확장자를 "p7s"로 변경하면 열어볼 수 있다.)



이 JCE를 사용하지 않아도, 자체적으로 구현해서 마음껏 암호학의 세계를 여행할 수 있지만, 시간에 쫓기는 분들을 위해서 간단히 사용법을 알아보도록 하자.

암호화에는 크게 블럭 암호화(block encryption)와 스트림 암호화(stream encryption)가 있는데, 여기서는 가장(?) 많이 쓰이는 블럭 암호화에 대해서 알아보도록 하겠다.
블럭 암호화는 말 그대로 데이터를 정해진 블럭으로 나눈후 해당 블럭을 암호화하는것이다.
대표적으로 DES/3DES/AES/SEED 등이 있다.

DES(Data Encryption Standard)는 Lucifer를 보완하여 IBM에서 개발한 블럭암호 알고리즘이다.
64비트 입력 블럭을 56비트 비밀키를 사용하여 암호화하는 알고리즘이다.
전세계적으로 널리 사용되었다가, 56비트라는 짧은 키(key)로 인해 안전하지 않다고 보는 견해가 많아져서, 요즘은 AES한테 밀리는 추세이다.

3DES(Triple Data Encryption Standard)는 DES의 단점을 보완하기 위해서 기존의 DES 방식을 3번 적용(암호화->복호화->암호화)시킨것으로 그 과정에 따라서 56비트의 배수로 암호화 복잡도가 증가한다고 한다.
이 3번의 암복호화 즉, Encryption->Decryption->Encryption을 첫글자를 따서 DESede라 명칭하기도한다.

AES(Advanced Encryption Standard)는 현재 미국 정부 표준으로 지정된 블럼 암호화 알고리즘으로서, DES를 대체하고 있다.
키(key)의 크기는 128, 160, 190, 224, 256비트를 사용할 수 있으며, 현재 미국 표준으로 인정받은 것은 128비트이다.
(JCE에서 제공하는것도 128비트밖에 안될지도...)

SEED는 한국정보보호진흥원을 중심으로 국내 암호 전문가들이 참여하여 만든, 순수 국내기술 블럭암호화 알고리즘이다.
(SEED는 다음에 한번 구현에해보기도 하고 오늘은 다루지 않겠다.)

블럭 암호화를 하기 위해서는 당연히 원문(plain text)이 있어야하고, 암화하 하기 위한 키(key)가 있어야한다.
그리고 블럭 암호화 운용모드에 따라서 IV(Initialization Vector)가 필요하기도 하다.
이 키(key)에 따라서 대칭키 암호화와 비대칭키 암호화로 나눌 수 있다.
대칭키 암호화는 암호화키와 복호화키가 동일하다. 속도가 빠른 장점이 있으니 키(key)를 분배하기 어렵다.(키가 누출되면 암호화 자체가 의미가 없어진다.)
비대칭키 암호화는 키(key) 분배 문제때문에 개발되었는데, 암호화와 복호화키가 다르다. 즉, 공개키(PublicKey), 개인키(PrivateKey)가 따로 생성된다.
간단히 설명하자면, 멀리 떨어진 시스템에서 사용할때에, 이 2가지를 결합하여 사용하고 있다.
대칭키 암호화에 사용할 키(key)를 생성한다음, 비대칭키 암호화를 이용해서 그 키를 분배한다.
키가 안전하게 분배되면 대칭키 암호화를 이용해서 서로 암호화된 문서를 주고 받고 하는것이다.
(비대칭키 암호화가 대칭키 암호화보다 느리기 때문에 키분배에만 사용한다.)
앞에 설명한 DEs/3DES/AES/SEED는 대칭키 암호화 알고리즘이다. 비대칭키 암호화 알고리즘은 RSA(Rivest Shamir Adleman)가 있다.(사람 이름 첫글자를 딴것임)

이제 한번 구현해보도록 하자.
(바이트들을 화면에 출력하기(?) 위해 ByteUtils 클래스를 사용하겠다. 첨부파일을 참조하도록 하자)

1. 암호화에 사용할 키(key) 만들기
- 키를 만드는 방법은 랜덤하게 동적으로 만드는 방법과, 정해진 키를 읽어와서 만드는 방법이 있다. 두 기능을 하는 메소드를 만들어보자.

01 package test.cipher;
02
03 import java.security.InvalidKeyException;
04 import java.security.Key;
05 import java.security.NoSuchAlgorithmException;
06 import java.security.spec.InvalidKeySpecException;
07 import java.security.spec.KeySpec;
08
09 import javax.crypto.Cipher;
10 import javax.crypto.KeyGenerator;
11 import javax.crypto.SecretKey;
12 import javax.crypto.SecretKeyFactory;
13 import javax.crypto.spec.DESKeySpec;
14 import javax.crypto.spec.DESedeKeySpec;
15 import javax.crypto.spec.IvParameterSpec;
16 import javax.crypto.spec.SecretKeySpec;
17
18 import kr.kangwoo.util.ByteUtils;
19 import kr.kangwoo.util.StringUtils;
20
21 public class CipherTest {
22
23 /**
24 * <p>해당 알고리즘에 사용할 비밀키(SecretKey)를 생성한다.</p>
25 *
26 * @return 비밀키(SecretKey)
27 */
28 public static Key generateKey(String algorithm) throws NoSuchAlgorithmException {
29 KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
30 SecretKey key = keyGenerator.generateKey();
31 return key;
32 }
33
34 /**
35 * <p>주어진 데이터로, 해당 알고리즘에 사용할 비밀키(SecretKey)를 생성한다.</p>
36 *
37 * @param algorithm DES/DESede/TripleDES/AES
38 * @param keyData
39 * @return
40 */
41 public static Key generateKey(String algorithm, byte[] keyData) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException {
42 String upper = StringUtils.toUpperCase(algorithm);
43 if ("DES".equals(upper)) {
44 KeySpec keySpec = new DESKeySpec(keyData);
45 SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithm);
46 SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
47 return secretKey;
48 } else if ("DESede".equals(upper) || "TripleDES".equals(upper)) {
49 KeySpec keySpec = new DESedeKeySpec(keyData);
50 SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithm);
51 SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
52 return secretKey;
53 } else {
54 SecretKeySpec keySpec = new SecretKeySpec(keyData, algorithm);
55 return keySpec;
56 }
57 }
58 }


위 코드는 일단 정상(?) 작동하는데, 맞게 구현하지는 아직 잘 모르겠다. (내공이 부족하여 혹시 틀린부분이 있으면 지적바란다.)

2. DES 암호화/복호화 해보기
- DES 암호화/복화화를 하기위해선 Cipher 클래스를 사용하면 된다.
- Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding"); 이런씩으로 "암호화알고리즘/운용모드/패딩"으로 사용하면 된다.
- DES는 암호화키가 64비트이므로 64비트 키를 생성하자
(예리한 분은 여기서 의문을 가질것이다. 위~에서 설명할때 DES는 56비트 키라고 했는데, 왜 갑자기 여기서는 64비트라고 우기는가에 대해서...
설명하자면, 64비트의 키(외부키) 중 56비트는 실제의 키(내부키)가 되고 나머지 8비트는 거사용(?) 비트로 사용된다고 한다.)

01 public static void main(String[] args) throws Exception {
02 Key key = generateKey("DES", ByteUtils.toBytes("68616e6765656e61", 16));
03
04 String transformation = "DES/ECB/NoPadding";
05 Cipher cipher = Cipher.getInstance(transformation);
06 cipher.init(Cipher.ENCRYPT_MODE, key);
07
08 String str = "hello123";
09 byte[] plain = str.getBytes();
10 byte[] encrypt = cipher.doFinal(plain);
11 System.out.println("원문 : " + ByteUtils.toHexString(plain));
12 System.out.println("암호 : " + ByteUtils.toHexString(encrypt));
13
14 cipher.init(Cipher.DECRYPT_MODE, key);
15 byte[] decrypt = cipher.doFinal(encrypt);
16 System.out.println("복호 : " + ByteUtils.toHexString(decrypt));
17 }


* 실행 결과
원문 : 68656c6c6f313233
암호 : 51d6aa8bcc176819
복호 : 68656c6c6f313233

실행해보면 암호화/복호화가 잘되는것을 알 수 있다.
DES는 암호화 알고리즘이고, ECB는 뭘까? 운용모드라고 했는데, http://blog.kangwoo.kr/13 여기에 퍼온글이 있으니 참조하기 바란다.
패딩(padding)은 말 그대로 패딩인데, 번역하면 채워넣기, 모자란만큼 채워넣는 역할을 한다. DES는 블럭 암호화 알고리즘이다.
그래서 암호화 할라면 블럭이 필요한데, DES 경우 64비트가 한 블럭을 형성한다. 그런데 입력한 데이터가 64비트가 안된다면 어떻게 될까?
궁금하면 "hello123" 을 "hello"으로 바꾼다음 실행해보자. 아마 다음과 같은 에러가 발생할 것이다.

* 실행 결과
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length not multiple of 8 bytes

입력한 데이터가 8 bytes 즉, 64비트가 아니니 배째라는것이다.
그러면 정상작동을 하기위해서는 블럭크기의 배수만큼 나머지 데이터를 채워줘야하는데 그 역할을 하는게 패딩이다.
"DES/ECB/NoPadding"을 "DES/ECB/PKCS5Padding"을 바꾼다음 실행해보자.
이제 데이터가 64비트가 아니어도 정상작동한다. 그래서 귀찮으면 가능한한 패딩을 해주게 하면 좋다.
(운용 모드가 OFB/CFB일 경우 NoPadding을 사용해도 상관없다. IV를 이용해서 처리할때 필요가 없어지기때문이다. 믿거나 말거나)


3. DESede 암호화/복호화 해보기
- DES와 별반 차이 없다. 키 크기가 64비트 * 3 = 192비트로 늘어난것밖에 없다.

01 public static void main(String[] args) throws Exception {
02 Key key = generateKey("DESede", ByteUtils.toBytes("696d697373796f7568616e6765656e61696d697373796f75", 16));
03
04 String transformation = "DESede/ECB/PKCS5Padding";
05 Cipher cipher = Cipher.getInstance(transformation);
06 cipher.init(Cipher.ENCRYPT_MODE, key);
07
08 String str = "hello123";
09 byte[] plain = str.getBytes();
10 byte[] encrypt = cipher.doFinal(plain);
11 System.out.println("원문 : " + ByteUtils.toHexString(plain));
12 System.out.println("암호 : " + ByteUtils.toHexString(encrypt));
13
14 cipher.init(Cipher.DECRYPT_MODE, key);
15 byte[] decrypt = cipher.doFinal(encrypt);
16 System.out.println("복호 : " + ByteUtils.toHexString(decrypt));
17 }


* 실행 결과
원문 : 68656c6c6f313233
암호 : ad44691609cb1e017378631303581279
복호 : 68656c6c6f313233

4. AES 암호화/복호화 해보기
- DES와 별반 차이 없다. 키 크기가 128비트인것을 제외하면 말이다.

01 public static void main(String[] args) throws Exception {
02 Key key = generateKey("AES", ByteUtils.toBytes("696d697373796f7568616e6765656e61", 16));
03
04 String transformation = "AES/ECB/PKCS5Padding";
05 Cipher cipher = Cipher.getInstance(transformation);
06 cipher.init(Cipher.ENCRYPT_MODE, key);
07
08 String str = "hello123";
09 byte[] plain = str.getBytes();
10 byte[] encrypt = cipher.doFinal(plain);
11 System.out.println("원문 : " + ByteUtils.toHexString(plain));
12 System.out.println("암호 : " + ByteUtils.toHexString(encrypt));
13
14 cipher.init(Cipher.DECRYPT_MODE, key);
15 byte[] decrypt = cipher.doFinal(encrypt);
16 System.out.println("복호 : " + ByteUtils.toHexString(decrypt));
17 }


* 실행 결과
원문 : 68656c6c6f313233
암호 : d5c5e1ffb734b610679f36c0e535fe39
복호 : 68656c6c6f313233
5. IV 사용하기.
- 블록 암호의 운용 모드(Block engine modes of operation)가 CBC/OFB/CFB를 사용할 경우에는 Initialization Vector(IV)를 설정해줘야한다. 왜냐면 사용하기 때문이다. AES 암호화/복호화 코드에서 운용 모드가 CBC로 변경해보자.

01 public static void main(String[] args) throws Exception {
02 Key key = generateKey("AES", ByteUtils.toBytes("696d697373796f7568616e6765656e61", 16));
03
04 String transformation = "AES/CBC/PKCS5Padding";
05 Cipher cipher = Cipher.getInstance(transformation);
06 cipher.init(Cipher.ENCRYPT_MODE, key);
07
08 String str = "hello123";
09 byte[] plain = str.getBytes();
10 byte[] encrypt = cipher.doFinal(plain);
11 System.out.println("원문 : " + ByteUtils.toHexString(plain));
12 System.out.println("암호 : " + ByteUtils.toHexString(encrypt));
13
14 cipher.init(Cipher.DECRYPT_MODE, key);
15 byte[] decrypt = cipher.doFinal(encrypt);
16 System.out.println("복호 : " + ByteUtils.toHexString(decrypt));
17 }


* 실행 결과 원문 : 68656c6c6f313233
암호 : 92e4fa9add0d4d5a07954207890e5b5c
Exception in thread "main" java.security.InvalidAlgorithmParameterException: Parameters missing
...생략...

실행해보면 암호화는 되는데, 복호화도중 파라메터가 없다고 에러가 발생한다. 예상한데로라면 암호화할때도 에러가 나야하는데, 불행히도 암호화는 정상적으로 작동한다. 왜냐면 암호화할때는 IV를 지정해주지 않으면, 자동적으로 랜덤 IV를 만들어서 사용해버린다.
정말인지 테스트해보자.

01 public static void main(String[] args) throws Exception {
02 Key key = generateKey("AES", ByteUtils.toBytes("696d697373796f7568616e6765656e61", 16));
03
04 String transformation = "AES/CBC/PKCS5Padding";
05
06 Cipher cipher = Cipher.getInstance(transformation);
07 cipher.init(Cipher.ENCRYPT_MODE, key);
08
09 String str = "hello123";
10 byte[] plain = str.getBytes();
11 byte[] encrypt = cipher.doFinal(plain);
12 System.out.println("원문 : " + ByteUtils.toHexString(plain));
13 System.out.println("암호 : " + ByteUtils.toHexString(encrypt));
14
15 cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(cipher.getIV()));
16 byte[] decrypt = cipher.doFinal(encrypt);
17 System.out.println("복호 : " + ByteUtils.toHexString(decrypt));
18 }


* 실행 결과
원문 : 68656c6c6f313233
암호 : 26c7d1d26c142de0a3b82f7e8f90860a
복호 : 68656c6c6f313233
new IvParameterSpec(cipher.getIV())을 이용해서 자동 생성된 IV를 초기화 파라메터를 넘겨주니 정상 작동한다.
실제 사용할때는 암호화 복호화가 따로(?) 작동하니 이렇게 구현하는건 불가능할것이다. IV의 크기는 블럭크기와 동일하므로 키처럼 생성해서 잘 관리하면 되겠다.

01 public static void main(String[] args) throws Exception {
02 Key key = generateKey("AES", ByteUtils.toBytes("696d697373796f7568616e6765656e61", 16));
03 byte[] iv = ByteUtils.toBytes("26c7d1d26c142de0a3b82f7e8f90860a", 16);
04 String transformation = "AES/CBC/PKCS5Padding";
05
06 IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
07 Cipher cipher = Cipher.getInstance(transformation);
08 cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);
09
10 String str = "hello123";
11 byte[] plain = str.getBytes();
12 byte[] encrypt = cipher.doFinal(plain);
13 System.out.println("원문 : " + ByteUtils.toHexString(plain));
14 System.out.println("암호 : " + ByteUtils.toHexString(encrypt));
15
16 cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);
17 byte[] decrypt = cipher.doFinal(encrypt);
18 System.out.println("복호 : " + ByteUtils.toHexString(decrypt));
19 }


* 실행 결과
원문 : 68656c6c6f313233
암호 : 1876c3ae98760ccf1821ea46fc9ce761
복호 : 68656c6c6f313233
6. 파일을 AES 암호화/복호화 해보기
- 이번에 파일을 읽어와서 암호화/복호화를 해보자. JavaIO를 다룰 수 있으면 간단히 구현할 수 있을것이다.

01 public static void main(String[] args) throws Exception {
02
03 Key key = generateKey("AES", ByteUtils.toBytes("696d697373796f7568616e6765656e61", 16));
04 String transformation = "AES/ECB/PKCS5Padding";
05
06 Cipher cipher = Cipher.getInstance(transformation);
07 cipher.init(Cipher.ENCRYPT_MODE, key);
08
09 File plainFile = new File("c:/plain.txt");
10 File encryptFile = new File("c:/encrypt.txt");
11 File decryptFile = new File("c:/decrypt.txt");
12
13 BufferedInputStream input = null;
14 BufferedOutputStream output = null;
15 try {
16 input = new BufferedInputStream(new FileInputStream(plainFile));
17 output = new BufferedOutputStream(new FileOutputStream(encryptFile));
18
19 int read = 0;
20 byte[] inBuf = new byte[1024];
21 byte[] outBuf = null;
22 while ((read = input.read(inBuf)) != -1) {
23 outBuf = cipher.update(inBuf, 0, read);
24 if (outBuf != null) {
25 output.write(outBuf);
26 }
27 }
28 outBuf = cipher.doFinal();
29 if (outBuf != null) {
30 output.write(outBuf);
31 }
32 } finally {
33 if (output != null) try {output.close();} catch(IOException ie) {}
34 if (input != null) try {input.close();} catch(IOException ie) {}
35 }
36
37 cipher.init(Cipher.DECRYPT_MODE, key);
38 try {
39 input = new BufferedInputStream(new FileInputStream(encryptFile));
40 output = new BufferedOutputStream(new FileOutputStream(decryptFile));
41
42 int read = 0;
43 byte[] inBuf = new byte[1024];
44 byte[] outBuf = null;
45 while ((read = input.read(inBuf)) != -1) {
46 outBuf = cipher.update(inBuf, 0, read);
47 if (outBuf != null) {
48 output.write(outBuf);
49 }
50 }
51 outBuf = cipher.doFinal();
52 if (outBuf != null) {
53 output.write(outBuf);
54 }
55 } finally {
56 if (output != null) try {output.close();} catch(IOException ie) {}
57 if (input != null) try {input.close();} catch(IOException ie) {}
58 }
59 }


"plain.txt"란 파일을 만들어 놓고 실행하면, "encrypt.txt"와 "decrypt.txt" 파일이 생성될것이다. 제대로 암호화 복호화되었는지 비교해보자.
7. 비밀번호 암호화
- 대부분의 사이트에서 비밀번호를 암호화한다.(불행히도 안하는곳도 많다.) 위에서 배운 블럭암호화 알고리즘으로 암호화를 한다면, 사이트 관리자들은 비밀번호를 다 알 수 있을것이다. (키를 해당 사이트에서 관리하므로, 키를 알면 복호화가 가능하다.) 정말 기분 나쁘다. 타인이 나의 비밀번호를 안다는것은... 그러면 관리자들도 모르게 암호화를 할려면 어떻게 해야할까?
아주 간단하다. 암호화에 사용하는 키 자체를 평문 즉, 비밀번호로 사용하면 된다. 아래 처럼 구현하면 본인만 알 수가 있는것이다. 암호화 할려는 비밀번호 자체가 두 역활을 동시에 하는것이다. 한번 구현해보자.

01 public static void main(String[] args) throws Exception {
02 String password = "mypassword";
03
04 byte[] passwordBytes = password.getBytes();
05 int len = passwordBytes.length;
06 byte[] keyBytes = new byte[16];
07 if (len >= 16) {
08 System.arraycopy(passwordBytes, 0, keyBytes, 0, 16);
09 } else {
10 System.arraycopy(passwordBytes, 0, keyBytes, 0, len);
11 for (int i = 0; i < (16 - len); i++) {
12 keyBytes[len + i] = passwordBytes[i % len];
13 }
14 }
15
16 Key key = generateKey("AES", keyBytes);
17 String transformation = "AES/ECB/PKCS5Padding";
18 Cipher cipher = Cipher.getInstance(transformation);
19 cipher.init(Cipher.ENCRYPT_MODE, key);
20
21
22 byte[] plain = password.getBytes();
23 byte[] encrypt = cipher.doFinal(plain);
24 System.out.println("원문 : " + ByteUtils.toHexString(plain));
25 System.out.println("암호 : " + ByteUtils.toHexString(encrypt));
26
27 cipher.init(Cipher.DECRYPT_MODE, key);
28 byte[] decrypt = cipher.doFinal(encrypt);
29 System.out.println("복호 : " + ByteUtils.toHexString(decrypt));
30 }


키는 패딩이 안되기 때문에 자체적으로 길이를 맞춰줘야한다. 여기서는 16바이트보다 크면 자르고, 16바이트보다 작으면 입력받는 비밀번호 문자를 반복해서 뒤에 붙여두는식으로 구현하였다.
이런 방식이 귀찮다면 블럭 암호화 방법 대신, MD5나 SHA1으로 메시지 해쉬값을 저장해서 그 값을 비교해도 된다. (이 방식은 복호화가 안된다.) my-sql password() 함수가 SHA1을 이용하는데, 다음번에 알아보도록 하자.



 

http://kangwoo.tistory.com/44

'ApplicationPrograming > Java' 카테고리의 다른 글

[HttpURLConnection] POST로 파라미터 넘기기  (0) 2015.04.24
Commons-Fileupload의 한글관련 문제 추가  (0) 2013.03.15
Commons-DbUtils  (0) 2013.03.15
Commons-Fileupload의 한글관련 문제 추가  (0) 2013.03.15
JDOM  (0) 2013.01.31
Posted by 물색없는세상
2013. 3. 15. 01:23

안녕하세요.. ^^

파일 업로드 찾다가 요기까지 왔네요.. ^^

샘플코드는 잘 봤습니다..

(샘플코드에 변수이름이 조금 잘못 되어 있는 부분이 있었습니다. - 큰문제는 아니죠.. ^^)

파일 업로드를 구현하면서 어려움(?)을 겪었던 부분과 해결 부분을 적어보려 합니다.

(저도 도움을 받았으니, 혹시나 저와 같은 어려움(?) 을 겪으신 분들을 위하여.. ^^)

* OS : Linux
* DB : Oracle
* WAS : OC4J 10g AS

/**
* form tag의 input name들이 아래와 같이 있습니다.
*/

String sMode = "";

String sPageNo = "";
String sKeyWord = "";
String sSearchPart = "";
String sSeqNum = "";

String sAnsTitle = "";
String sAnsContent = "";

String sFileName = "";
String sTmpFileName = "";

try {

//Multipart로 넘어왔는가?
if (FileUpload.isMultipartContent(req)) {

DiskFileUpload dfuUpload = new DiskFileUpload();

/**
* 이 부분 상당히 애를 먹었습니다.
* Windows 환경에서는 문제가 없는데,
* 개발 서버(Linux)로 올리게 되면 계속해서 한글이 깨지더군요.
*
* 그래서, 아래와 같이 DiskFileUpload의 HearderEncoding을 해주었습니다.
*/

dfuUpload.setHeaderEncoding("EUC_KR");

List lsItems = dfuUpload.parseRequest(req);
Iterator iterator = lsItems.iterator();

while (iterator.hasNext()) {

FileItem fItem = (FileItem) iterator.next();

/**
* 넘어오는 form이 multipart일때도 있고 아닐 경우도 있어서
* 처리한 부분입니다.
*
* HashMap을 사용해서 put한 다음에, 필요할때 꺼내 써도 무방할듯 합니다.
*/

if ("mode".equals(fItem.getFieldName())) {
sMode = fItem.getString();
} else if ("pageNo".equals(fItem.getFieldName())) {
sPageNo = fItem.getString();
} else if ("keyWord".equals(fItem.getFieldName())) {
//한글처리
sKeyWord = fItem.getString("EUC_KR");
} else if ("searchPart".equals(fItem.getFieldName())) {
sSearchPart = fItem.getString();
} else if ("seqNum".equals(fItem.getFieldName())) {
sSeqNum = fItem.getString();
} else if ("ansTitle".equals(fItem.getFieldName())) {
//한글처리
sAnsTitle = fItem.getString("EUC_KR");
} else if ("ansContent".equals(fItem.getFieldName())) {
//한글처리
sAnsContent = fItem.getString("EUC_KR");
}

//파일 타입 폼필드라면
if (!fItem.isFormField()) {

if (fItem.getSize() > 0) {
//파일 이름을 가져온다
//- 위에서 dfuUpload.setHeaderEncoding("EUC_KR")를 안해 줬더니

// 파일이름이 자꾸 깨지더군요.
sFileName = fItem.getName().substring(fItem.getName().lastIndexOf("\\") + 1);

try {
/**
* upload변수는 servlet에서 init 할때 아래와 같이 선언해 주었습니다.
* upload = config.getServletContext().getRealPath("/upload/suggest/");
*/

File file = new File(upload + sFileName);
fItem.write(file);
} catch (IOException e) {
System.out.println(e);
}
}
}

}

} else {
//multipart가 아닐때 사용되는 parameter들을 가지고 옴

//WebUtil은 null Check를 위한 util입니다. - 빼도 상관없습니다.
sMode = WebUtil.nvl((String) req.getParameter("mode"), "");
sPageNo = WebUtil.nvl((String) req.getParameter("pageNo"), "");
sKeyWord = WebUtil.nvl((String) req.getParameter("keyWord"), "");
sSearchPart = WebUtil.nvl((String) req.getParameter("searchPart"), "");
sSeqNum = WebUtil.nvl((String) req.getParameter("seqNum"), "");
sAnsTitle = WebUtil.nvl((String) req.getParameter("ansTitle"), "");
sAnsContent = WebUtil.nvl((String) req.getParameter("ansContent"), "");

}
} catch (Exception e) {
LogManager.getLogger("error").error(e.toString());
}

 

http://levin01.blog.me/100011050050

'ApplicationPrograming > Java' 카테고리의 다른 글

[HttpURLConnection] POST로 파라미터 넘기기  (0) 2015.04.24
자바 암호화  (0) 2015.02.25
Commons-DbUtils  (0) 2013.03.15
Commons-Fileupload의 한글관련 문제 추가  (0) 2013.03.15
JDOM  (0) 2013.01.31
Posted by 물색없는세상
2013. 3. 15. 01:21

Commons-DbUtils

I. 어디서 다운을 받나요?

http://jakarta.apache.org/site/downloads/downloads_commons-dbutils.cgi

II. 설치는 어떻게 하나요?

다운 받은 commons-beanutils.jar는 자신의 /WEB-INF/lib/ 에 복사합니다

III. DbUtils란 무엇인가요?

DbUtils는 JDBC 작업을 좀더 쉽게 할수있도록 해주는 작은 클래스 집합입니다.

IV. 왜 DbUtils를 사용해야 하는가요?

① resource 누출에 대한 어떠한 가능성도 없습니다
JDBC코딩을 하는데 있어서 쉽지않고 양도 만만치 않으며 지루해 지기 쉽습니다
이러다 보면 자기도 모르게 Connection 누수를 발생시킬수 있는데 이러한 가능성을 배재해 줍니다


② 코드의 가독성이 높아집니다
데이터베이스 처리하는데 필요한 코드의 양을 절대적으로 줄여야 합니다.
남아있는 코드로 당신의 의도를 정확하게 나타내어야 합니다.


③ ResultSet으로 부터 JavaBean property로 세팅을 해줍니다!
더이상 setter메소드를 이용하여 ResultSet으로부터 컬럼값을 가져오는 코딩을 하지 않아도 됩니다
ResultSet 각각의 row는 bean instance의 에 완벽하게 전달해 줍니다

V. 어떻게 사용하나요?

Connection, Statement, ResultSet 의 close를 간단하게!

이럴때는 org.apache.commons.dbutils.DbUtils 클래스를 이용하자!

이 클래스는 모두 static 메소드들로 구성되어있습니다

사용예)

DbUtils.close(conn);
DbUtils.close(stmt);
DbUtils.close(rs);
DbUtils.closeQuietly(conn);
DbUtils.closeQuietly(stmt);
DbUtils.closeQuietly(rs);
DbUtils.closeQuietly(conn, stmt, rs);
DbUtils.commitAndClose(conn);

DbUtils.commitAndCloseQuietly(conn);
DbUtils.loadDriver("com.mysql.jdbc.Driver");
DbUtils.rollback(conn);

closeQuietly 메소드처럼 뒤에 Quietly라고 붙어 있는 메소드는 익셉션 처리는 자체적으로 처리합니다,

즉 자신을 call한곳으로 throw 하지 않습니다

commitAndCloses는 connection을 commit 후 close 하며 rollback는 connection을 rollback 합니다

loadDriver 는 JDBC 드라이버를 로딩 합니다

파일로 저장된 SQL을 사용하자!

이럴 때는 org.apache.commons.dbutils.QueryLoader 클래스를 이용합니다

이 클래스는 SingleTone 패턴의 클래스입니다

즉 파일로 저장된 SQL을 읽어 HashMap으로 로드하는 클래스 입니다

사용예)

QueryLoader queryloader = QueryLoader.getInstance(); //싱글톤
HashMap hashmap = queryloader.load("sql");

queryloader.upload("sql");

queryloader는 싱클톤이므로 위와같이 객체를 얻어옵니다

load 함수는 Properties 클래스를 이용하여 sql.properties 파일을 읽어

HashMap으로 저장하여 리턴하여 줍니다

unload는 load시 따로 메모리에 저장해 놓았던 sql 정보를 해제합니다

Setter함수로 더이상 머리 아프지 말자!

이럴때는 org.apache.commons.dbutils.QueryRunner 클래스를 이용합니다

사용예)

...

BoardVO boardVO = null;

ArrayList arraylist = new ArrayList();

resultset = statement.executeQuery("SELECT * FROM board_t");

while (resultset.next()) {

boardVO = new BoardVO();

boardVO.setTitle("title");

boardVO.setContent("content");

boardVO.setWriter("writer");

arraylist.add(boardVO);

}

..

와 같은 코드는 다음과 같이 간략화 됩니다

ResultSetHandler rsh= new BeanListHandler(BoardVO.class);
QueryRunner queryRunner = new QueryRunner();

List list = (List)queryRunner.query(conn, "SELECT * FROM board_t", rsh);

정말 간단해 집니다 만약 테이블에 컬럼이 30~40개가 된다면..

select 한문장 할려면 코드수가 몇십줄 입니다. 더이상 노가다 하지 맙시다~

QueryRunner는 다음과 같은 함수를 지원합니다

사용예)

QueryRunner queryrunner = new QueryRunner();
QueryRunner queryrunner = new QueryRunner(DataSource ds);
//datasource를 바로 이용할 수 있다

queryRunner.query(Connection conn, String sql, ResultSetHandler rsh)
queryRunner.query(Connection conn, String sql, Object param, ResultSetHandler rsh)
queryRunner.query(Connection conn, String sql, Object[] params, ResultSetHandler rsh)

여기서 말하는 Object param은 파라미터 전달시 사용됩니다

ArrayList params = new ArrayList();
params.add("100");

params.add("200");

ResultSetHandler rsh = new BeanListHandler(BoardVO.class);
QueryRunner queryRunner = new QueryRunner();

List list = (List)queryRunner.query(conn, "SELECT * FROM board_t WHERE boardNo > ? and boardNo < ?", params.toArray(), rsh);

select 뿐만 아니라 update, delete역시 가능합니다

사용예)

QueryRunner queryRunner = new QueryRunner();

queryRunner.update(Connection conn, String sql)
queryRunner.update(Connection conn, String sql, Object param)
queryRunner.update(Connection conn, String sql, Object params[])

ArrayList params = new ArrayList();
params.add(boardId);
queryRunner.update(connection, "UPDATE board_t SET read = read + 1 WHERE boardNo = ?", params.toArray());

와 같이 사용할 수 있습니다

VI. 샘플코드

public class DbUtilsExample() {

public static void main(String[] args) {

HashMap map = QueryLoader.getInstance().load("sql"); // (주의) load함수는 실행할때마다 파일을 읽습니다

Connection conn = null;

try {

DbUtils.loadDriver("com.mysql.jdbc.Driver");

conn = DriverManager.getConnection("jdbc:mysql://localhost/mysql", "root", "");

ArrayList params = new ArrayList();
params.add(args[0]);

ResultSetHandler rsh = new BeanListHandler(BoardVO.class);

QueryRunner qr = new QueryRunner();

List list = qr.query(conn, (String)map.get("select"), params.toArray(), rsh);

for (int i = 0; i < list.size(); i++) {

BoardVO board = (BoardV)list.get(i);

System.out.println(board.getTitle());

System.out.println(board.getContent());

System.out.println(board.getWriter());

)

} catch (Exception e) {

System.out.println(e);

} finally {

DbUtils.closeQuitely(conn);

}

}

}

 

http://levin01.blog.me/100011050694

'ApplicationPrograming > Java' 카테고리의 다른 글

자바 암호화  (0) 2015.02.25
Commons-Fileupload의 한글관련 문제 추가  (0) 2013.03.15
Commons-Fileupload의 한글관련 문제 추가  (0) 2013.03.15
JDOM  (0) 2013.01.31
me2DAY XML Parsing (DOM) - 자바 XML파싱  (0) 2012.07.24
Posted by 물색없는세상
2013. 3. 15. 01:18

Commons-Fileupload

I. Commons-fileupload 란?

커먼스의 파일 업로드 패키지는 사용법이 쉬우며 쓸만한 함수들을 많이 지원합니다.

SmartUpload, MultipartRequest, Commons fileupload등을 모두 사용해 보았지만 개인적으로 가장 애착이 가는 파일 업로드입니다.

아쉬운점은 Commons-fileupload는 지난 2003년 6월 1.0버젼으로 정식 릴리즈 되었지만 그 이후로 이렇다 할 패치나 보안이 전혀 안되었다는 것입니다. 이말은 파일 업로드 자체가 그리 복잡한 패키지가 아니며 첫 정식 버젼이 그만큼 완벽하다는 것을 반증하는 말이기도 하겠지요 ^^

II. 다운로드 및 설치

자 다운로드 받으러 갑시다.

http://jakarta.apache.org/site/downloads/downloads_commons-fileupload.cgi

설치는 역시나 /WEB-INF/lib/ 폴더에 복사합니다.

III. 파일 업로드 처리 단계

먼저 파일 업로드된 아이쳄을 처리하기 전에 유효한 Request인지 확인해야 합니다.

boolean isMultipart = FileUpload.isMultipartContent(request);

이 코드는 현재 request가 multipart/form-data로 데이터를 전송했는지 유무를 true/false로 반환합니다.

업로드된 아이템이 매우 작다면 메모리에서 처리합니다.

큰 아이템이라면 임시 파일을 만들어 디스크에 저장하여 처리합니다.

너무 큰 아이템이라면 당연히 거부해야 합니다.

그렇지 않고 디폴트로 설정한 메모리, 최대값의 범위를 초과하지 않는다면 업로드를 시작합니다.

② ~ ⑤ 까지를 다음과 같이 간단히 할 수 있습니다.

// 파일 업로드 핸들러를 생성
DiskFileUpload upload = new DiskFileUpload();

// 한번에 메모리에 저장할 사이즈 설정

upload.setSizeThreshold(yourMaxMemorySize);

// 파일 업로드 최대 사이즈를 설정

upload.setSizeMax(yourMaxRequestSize);

// 파일 업로드 경로를 설정
upload.setRepositoryPath(yourTempDirectory);

이제 전송된 request를 parsing하여 아이템들을 추출합니다.

List /* FileItem */ items = upload.parseRequest(request);

위에서 설정한 값들을 request parsing단위로 따로 설정 할 수 있습니다.

List /* FileItem */ items = upload.parseRequest(request,
yourMaxMemorySize, yourMaxRequestSize, yourTempDirectory);

이렇게 추출된 items를 이제 폼필드 타입에 따라 적절히 처리 합니다.

Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();

// 파일 타입이 아닌 다른 폼필드라면
if (item.isFormField()) {
processFormField(item);

// 파일 타입 폼필드라면
} else {
processUploadedFile(item);
}
}

폼필드에 따른 처리

만약 폼필드가 파일 이외의 파라미터라면 다음과 같이 처리하면 됩니다.

if (item.isFormField()) {
//파라미터 이름

String name = item.getFieldName();

//파라미터 값

String value = item.getString();
...
}

만약 폼필드가 파일 파라미터라면 다음과 같이 처리하면 됩니다.

if (!item.isFormField()) {

// 파라미터 이름
String fieldName = item.getFieldName();

// 파일 이름 (경로포함)
String fileName = item.getName();
String contentType = item.getContentType();

// 업로드한 파일이 메모리에 저장된 상태면 true, 거렇지 않고 임시 파일로 저장된 경우 false를 리턴
boolean isInMemory = item.isInMemory();

// 파일사이즈
long sizeInBytes = item.getSize();
...

// 파일 객체를 하나 만들어 업로드 완료!

File uploadedFile = new File(...);

item.write(uploadedFile);
}

파일 업로드 방식

파일 업로드 방식에는 모두 3가지가 존해합니다.

-. FileItem.write(File file);

-. FileItem.getInputStream();

-. FileItem.get()

write 는 직접적으로 업로드한 파일을 저장하는 가장 일반적이고 간단한 방식입니다.

나머지 두 방식은 스트림을 사용하여 업로드한 파일이 어떤 특별한 처리를 할 때 사용합니다. 혹은 데이터베이스에 바로 저장 할 경우에도 사용될 수 있습니다. 특히 get()은 메모리에 모두 할당하여 작업을 하니 주의를 요합니다.

VI. 샘플코드

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;

public class UploadServlet extends HttpServlet {

String upload = null;


public void init(ServletConfig config) throws ServletException {
super.init(config);
upload = config.getServletContext().getRealPath("/upload/")
}

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

try {

if (FileUpload.isMultipartContent(request)) {

DiskFileUpload fileUload = new DiskFileUpload();
fileUpload.setRepositoryPath(upload);
fileUpload.setSizeMax(100*1024*1024);
fileUpload.setSizeThreshold(1024*50);

List items = fileUpload.parseRequest(request);

Iterator iterator = items.iterator();
while (iterator.hasNext()) {


FileItem item = (FileItem) iterator.next();

if (!item.isFormField()) {
if (fileItem.getSize() > 0) {
//파일 이름을 가져온다
String filename = fileItem.getName().substring(fileItem.getName().lastIndexOf("\\")+1);

try {
File file = new File(upload+filename);
fileItem.write(file);
} catch (IOException e) {
System.out.println(e);
}
}
}
}
}

} catch (org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException e) {

// 파일 사이즈 초과시 발생하는 익셉션
System.out.println("파일 사이즈가 100메가 보다 더 초과되었습니다");
} catch (Exception e) {
System.out.println("업로드시 예기치 못한 오류가 발생하였습니다");
}
}
}

 

http://levin01.blog.me/100011050050

'ApplicationPrograming > Java' 카테고리의 다른 글

Commons-Fileupload의 한글관련 문제 추가  (0) 2013.03.15
Commons-DbUtils  (0) 2013.03.15
JDOM  (0) 2013.01.31
me2DAY XML Parsing (DOM) - 자바 XML파싱  (0) 2012.07.24
랜덤 숫자 생성  (0) 2012.06.11
Posted by 물색없는세상
2013. 1. 31. 16:34

라이브러리 다운로드 : http://www.jdom.org/

★ XML 생성 순서

1. Document 생성

: Document document = new Document();

2. root Element 생성

: Element root = new Element("루트");

3. 기타 Element 생성

: Element e1 = new Element("첫번째엘리먼트");

4. Document에 root 추가

: document.setRootElement(root);

5. 상위 노드에 하위 노드 추가

: root.addContent(e1);

6. 상위 노드에 값 데이터 설정

: e1.setText("값");

결과 - 일단은 Document 타입

<?xml version="1.0" encoding="UTF-8"?>
<루트><첫번째엘리먼트>값</첫번째엘리먼트></루트>

★ XMLOutputter를 이용항 Document 객체 출력

1. XMLOutputter 객체 생성

XMLOutputter outputter = new XMLOutputter();

2. Format 객체 생성 - 가독성을 고려

Format format = outputter.getFormat();

2-1. 인코딩, 들여쓰기, 줄바꿈 설정

format.setEncoding("UTF-8");

format.setIndent("\t");

format.setLineSeparator(LineSeparator.DEFAULT);

2-2. textMode 설정

format.setTextMode(Format.TextMode.NORMALIZE);

※ PRESERVE : 원본 유지

TRIM : 좌우 여백 제거

NORMALIZE : 좌우 여백 제거 + 내부 여백을 한칸 여백으로

TRIM_FULL_WHITE :

3. XMLOutputter 에 Format 설정

outputter.setFormat(format);

4. 객체 출력

String으로 출력

: outputter.outputString(document);

표준 출력

: outputter.output(document, System.out);

파일로 출력

: outputter.output(document, new FileoutputStream("이름.확장자");

: outputter.output(document, new FileWriter("이름.확장자");

소켓으로 출력

: stream = new DataOutputStream(소켓.getOutputStream());

outputter.output(document, stream);

결과 - String 타입으로 변환. 가독성이 높아짐.

<?xml version="1.0" encoding="UTF-8"?>

<루트>

<첫번째엘리먼트>값</첫번째엘리먼트>

</루트>

 

http://blog.naver.com/comkwang17/140176506563

'ApplicationPrograming > Java' 카테고리의 다른 글

Commons-DbUtils  (0) 2013.03.15
Commons-Fileupload의 한글관련 문제 추가  (0) 2013.03.15
me2DAY XML Parsing (DOM) - 자바 XML파싱  (0) 2012.07.24
랜덤 숫자 생성  (0) 2012.06.11
jar파일 실행  (0) 2012.05.04
Posted by 물색없는세상
2012. 7. 24. 17:00

XML(Extensible Markup Language)을 아시나요?
현재 XML파싱 기법을 알기 전에 XML이 무엇인지 모른다면 아래 사이트를 방문.
XML에 대한 기초적인 정보를 습득하신 후에 보시는 것을 추천합니다.

http://en.wikipedia.org/wiki/XML

위와 같은 XML문서를 Java에서 어떻게 데이터를 가져오는가에 대해 설명을 하고자
이렇게 포스팅을 작성합니다.

API를 보시면 javax.xml.parsers라는 패키지 내에
{

API의 경우 영어로 보시는 것을 추천하나, 만약 보기가 힘들경우 rath님께서 번역하신
번역본을 이용하시면 되겠습니다.
- API 주소 : http://xrath.com
}
DocumentBuilder
DocumentBuilderFactory
SAXParser
SAXParserFactory
위 4가지의 클래스가 존재하는데 클래스명에서 알수 있듯이 DOM에서는

DocumentBuilder
DocumentBuilderFactory 

위 2가지의 클래스로 XML의 데이터를 불러올 수 있습니다.
(물론, me2DAY와 같이 데이터가 큰 경우는 SAX방식이 더 효율적일 수 있습니다)
또, org.w3c.dom 패키지에 있는 Document 클래스도 필요로 합니다.

그럼 파싱할 XML이 필요하겠죠?
미투데이 API를 보니 자신이 적은 글에 대한 xml을 제공합니다.

주소는 http://me2day.net/api/get_posts/미투데이아이디.xml 가 됩니다.
저의 미투데이 아이디는 feato이니.

http://me2day.net/api/get_posts/feato.xml이 되는것이지요.

이제 본격적으로 게시물을 불러오는 프로그램을 만들어보겠습니다.
우선 파싱할 XML의 형태를 분석해야합니다.

<body>티스토리에서는 code block 은 지원되지 않나보네요?</body>

위 코드를 살펴보면 body라는 곳에 포스팅한 글이 출력된다는 것을 확인 할 수 있습니다.
그럼 위 데이터를 출력하는 프로그램을 작성해보도록 합시다.

위에서 확인했던 클래스를 import합니다.

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;

그 후 Main Method에 아래와 같이 코드를 작성합니다.

 Document me2Day_Document;
 DocumentBuilderFactory me2Day_dbf;

   // DOM트리를 구성합니다.
   me2Day_dbf  = DocumentBuilderFactory.newInstance();
   DocumentBuilder me2Day_db = me2Day_dbf.newDocumentBuilder();
   me2Day_Document = me2Day_db.parse("http://me2day.net/api/get_posts/feato.xml");
   // DOM Document에 객체를 가져옵니다.

이때 DOM의 장점이라고 할 수 있는 Node를 불러와야합니다.
Node는 import org.w3c.dom.NodeList; 후 사용이 가능합니다.

위의 클래스를 추가해준 후에 아래와 같이 코딩을 합니다.

NodeList Body = me2Day_Document.getElementsByTagName("body");

아까 위에서 보았던, body라는 태그명으로 데이터를 가져오겠다는 의미가 됩니다.

그러면 반복을 하기전에 해당 노드의 데이터가 몇개인지 확인해야겠지요?
아래와 같이 int형 변수에 Node의 길이를 저장합니다.

int Max_Node = Body.getLength();

그리고 반복을 통해 변화되는 데이터를 가져와야겠지요?

for(int Loop = 0; Loop < Max_Node; Loop++){

}

그럼, 위 반복문 내에는 어떤 코드가 들어가야 할까요?
해당 노트의 엘리먼트를 가져와야하는데 그러기 위해서는 아래 클래스를 추가해야한다.

import org.w3c.dom.Element;

Element클래스의 Method로써 Node.item(Loop)를 Element로 캐스팅한다.
즉, 카운터가 증가할때마다 한칸씩 이동하며, 다른 노드의 데이터를 가져온다.

Text는 역시 import org.w3c.dom.Text;를 추가해준 후 사용할 수 있는데.
XML문서가 이용이 가능한 상태에서는 Text노트를 통해 블록마다 첫번째 자식노드를 가져온다.

즉, Loop가 1일때, Element에 있는 첫번째 노드를 가져오겠다는 의미가 된다.

그 후 String변수를 통해 해당 Text를 getData();만 한다면 이상없이 DOM파싱이 완료된 것입니다.

for(int Loop = 0; Loop < Max_Node; Loop++){
    Element Me2DE = (Element)Body.item(Loop);
    Text Me2TXT= (Text)Me2DE .getFirstChild();
    String Me2Data= Me2TXT.getData();
    System.out.println(str);
  }
위의 모든 소스코드를 합치면, 이상없이 미투데이의 글이 출력되는 것을 확인할 수 있습니다.

 

http://kimeunseok.tistory.com/25

 

그외 참고

===================================================================

 

http://blog.naver.com/psymarin1/120112814386

http://ztest01.tistory.com/95

 

=====================================================================

 

package vvsp.utils;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

 

public class XMLProcessor {

 public XMLProcessor() {
  // TODO Auto-generated constructor stub
 }

  public static void main(String[] args) {
  // TODO Auto-generated method stub
  //File docFile = new File("orders.xml");
  Document doc = null;
  try {

   DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
   // DOM트리를 구성.
   DocumentBuilder db = dbf.newDocumentBuilder();
   //doc = db.parse(docFile);
   doc = db.parse("http://me2day.net/api/get_posts/내아이디.xml");
   NodeList body = doc.getElementsByTagName("body");
   for(int i=0; i<body.getLength(); i++){
    Element element = (Element)body.item(i);
    Text text = (Text)element .getFirstChild();
    String Me2Data = text.getData();
    System.out.println(Me2Data);
   }

  } catch (javax.xml.parsers.ParserConfigurationException pce) {

   System.out.println("The parser was not configured correctly.");
   System.exit(1);

  } catch (java.io.IOException ie) {

   System.out.println("Cannot read input file.");
   System.exit(1);

  } catch (org.xml.sax.SAXException se) {

   System.out.println("Problem parsing the file.");
   System.exit(1);

  } catch (java.lang.IllegalArgumentException ae) {

   System.out.println("Please specify an XML source.");
   System.exit(1);

  } catch (Exception e) {

   System.out.print("Problem parsing the file: "+e.getMessage());

  }
 }

}

'ApplicationPrograming > Java' 카테고리의 다른 글

Commons-DbUtils  (0) 2013.03.15
Commons-Fileupload의 한글관련 문제 추가  (0) 2013.03.15
JDOM  (0) 2013.01.31
랜덤 숫자 생성  (0) 2012.06.11
jar파일 실행  (0) 2012.05.04
Posted by 물색없는세상