레이블이 java인 게시물을 표시합니다. 모든 게시물 표시
레이블이 java인 게시물을 표시합니다. 모든 게시물 표시

2017년 5월 7일 일요일

how to change byte to hex string or hex string to byte in java (자바에서 hex string , byte간 변환)


데이터 전송을 위한 encode시 유용하게 사용하는 오래된 코드인데 정리해봤습니다. 제가 만든 예제에서도 가끔 사용하는 코드입니다.
Hex <-> Byte 간 형변환을 하는 코드입니다.
이러한 코드가 필요한 이유는 Byte가 화면에 출력 할 수 없는 경우가 있어서 이것을 다른쪽으로 데이터를 전달해 줄 때 화면상 어려움이 있어서 hex string형태로 변환하고 받는쪽에서는 다시 byte로 변환해서 사용하게됩니다.


예제
package testProject;

public class HexToByte {
 public static void main(String[] args) {
  String data = "Hello.";
  byte bdata[] = data.getBytes();
  String hex = byteArrayToHex(bdata);

  System.out.println(data);
  System.out.println(hex);

  byte bdataresult[] = hexToByteArray(hex);
  String data2 = new String(bdataresult);

  System.out.println(data2);
 }


 // hex string to byte[]
 public static byte[] hexToByteArray(String hex) {
  if (hex == null || hex.length() == 0) {
   return null;
  }
  byte[] ba = new byte[hex.length() / 2];
  for (int i = 0; i < ba.length; i++) {
   ba[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
  }
  return ba;
 }

 // byte[] to hex sting
 public static String byteArrayToHex(byte[] ba) {
  if (ba == null || ba.length == 0) {
   return null;
  }
  StringBuffer sb = new StringBuffer(ba.length * 2);
  String hexNumber;
  for (int x = 0; x < ba.length; x++) {
   hexNumber = "0" + Integer.toHexString(0xff & ba[x]);
   sb.append(hexNumber.substring(hexNumber.length() - 2));
  }
  return sb.toString();
 } 
}

결과
Hello.
48656c6c6f2e
Hello.

2017년 4월 30일 일요일

Java 시간 함수를 사용 하는 2가지 방법 (time function in Java) Date, Calendar


Date, Calendar


Java에서 시간을 사용하는 2가지 방법에 대해서 정리하였습니다.
Date, Calendar인데 Date에 대응되는 메소드는 추천하지 않는다고 API 도움말에 나와있습니다. 그리고 대부분은 Date 메소드는 Deprecated. 가 붙어있습니다.
https://docs.oracle.com/javase/7/docs/api/java/util/Date.html
The corresponding methods in Date are deprecated.

예제
package testProject;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class Test {

 public static void main(String[] args) {

     System.out.println("1 ======================");
     Date curdate1 = new Date();
     System.out.println("Date:"+curdate1);
     
     Calendar curcal1 = Calendar.getInstance();
     System.out.println("Calendar:"+curcal1);

     System.out.println("2 ======================");
     
     // Calendar to Date 1 
     Date curdate2 = curcal1.getTime();
     System.out.println("Calendar to Date M1:"+curdate2);
     
     // Calendar to Date 2
     Date curdate3 = new Date(curcal1.getTimeInMillis());
     System.out.println("Calendar to Date M2:"+curdate3);
     
     // Date to Calendar
     Calendar caldatetest = Calendar.getInstance();
     caldatetest.set(2000, 1/*Month:0-Jan, 1-Feb...*/, 1);
     System.out.println("set Calendar 2000/1/1:"+caldatetest.getTime());
     caldatetest.setTime(curdate2);
     System.out.println("Date to Calendar:"+caldatetest.getTime());
     
     System.out.println("3 ======================");
     
     // Date toString()
     System.out.println("Date.toString():"+curdate3.toString());
     
     // Date.setTime
     curdate2.setTime(0);
     System.out.println("Date.seTime(0):"+curdate2);
     
     curdate2.setTime(1000);
     System.out.println("Date.seTime(1000):"+curdate2);
     
     System.out.println("4 ======================");
     
     // Print date Format
     SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     System.out.println("SimpleDateFormat.format:"+format.format(curdate1));
     
     // Load data format
     Date savedDate = new Date(0);
     try {
      savedDate = format.parse("2017-04-17 00:17:29");
  } catch (java.text.ParseException e) {
   e.printStackTrace();
  }
     System.out.println("SimpleDateFormat.parse:"+format.format(savedDate));
     

     System.out.println("5 ======================");
     
     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     Calendar afterDate = Calendar.getInstance();
     Calendar beforeDate = Calendar.getInstance();
     Calendar baseDate = Calendar.getInstance();
     afterDate.set(2000, 2, 3, 4, 5);
     baseDate.set(2000, 1, 2, 3, 4);
     beforeDate.set(2000, 0, 1, 2, 3);
     System.out.println("afterDate:"+dateFormat.format(afterDate.getTime()));
     System.out.println("baseDate:"+dateFormat.format(baseDate.getTime()));
     System.out.println("beforeDate:"+dateFormat.format(beforeDate.getTime()));
     if(baseDate.before(beforeDate)){
      System.out.println("before true:"+dateFormat.format(beforeDate.getTime()));
     }
     if(baseDate.before(afterDate)){
      System.out.println("before true:"+dateFormat.format(afterDate.getTime()));
     }
     if(baseDate.after(beforeDate)){
      System.out.println("after true:"+dateFormat.format(beforeDate.getTime()));
     }
     if(baseDate.after(afterDate)){
      System.out.println("after true:"+dateFormat.format(afterDate.getTime()));
     }
 }


}

결과
1 ======================
Date:Sun Apr 30 15:18:06 KST 2017
Calendar:java.util.GregorianCalendar[time=1493533086063,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Seoul",offset=32400000,dstSavings=0,useDaylight=false,transitions=22,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=3,WEEK_OF_YEAR=18,WEEK_OF_MONTH=6,DAY_OF_MONTH=30,DAY_OF_YEAR=120,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=3,HOUR_OF_DAY=15,MINUTE=18,SECOND=6,MILLISECOND=63,ZONE_OFFSET=32400000,DST_OFFSET=0]
2 ======================
Calendar to Date M1:Sun Apr 30 15:18:06 KST 2017
Calendar to Date M2:Sun Apr 30 15:18:06 KST 2017
set Calendar 2000/1/1:Tue Feb 01 15:18:06 KST 2000
Date to Calendar:Sun Apr 30 15:18:06 KST 2017
3 ======================
Date.toString():Sun Apr 30 15:18:06 KST 2017
Date.seTime(0):Thu Jan 01 09:00:00 KST 1970
Date.seTime(1000):Thu Jan 01 09:00:01 KST 1970
4 ======================
SimpleDateFormat.format:2017-04-30 15:18:06
SimpleDateFormat.parse:2017-04-17 00:17:29
5 ======================
afterDate:2000-03-03 04:05:06
baseDate:2000-02-02 03:04:06
beforeDate:2000-01-01 02:03:06
before true:2000-03-03 04:05:06
after true:2000-01-01 02:03:06


예제 설명

초기화

Date, Calendar 를 초기화 하는 방법 입니다. 두개 모두 기본값으로 현재 시각을 가져옵니다.

     System.out.println("1 ======================");
     Date curdate1 = new Date();
     System.out.println("Date:"+curdate1);
     
     Calendar curcal1 = Calendar.getInstance();
     System.out.println("Calendar:"+curcal1);

결과

1 ======================
Date:Sun Apr 30 14:38:26 KST 2017
Calendar:java.util.GregorianCalendar[time=1493530706461,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Seoul",offset=32400000,dstSavings=0,useDaylight=false,transitions=22,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=3,WEEK_OF_YEAR=18,WEEK_OF_MONTH=6,DAY_OF_MONTH=30,DAY_OF_YEAR=120,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=2,HOUR_OF_DAY=14,MINUTE=38,SECOND=26,MILLISECOND=461,ZONE_OFFSET=32400000,DST_OFFSET=0]


변환

Calendar를 Date로 변환하는 방법입니다. getTime(), getTimeMillis() 메소드를 이용하면 됩니다. 반대로 Date를 Calendar로 변환 하는 방법은 setTime()메소드를 사용하면 됩니다.
Calendar.set시 월의 경우 0이 1월입니다.
  • A year y is represented by the integer y - 1900.
  • A month is represented by an integer from 0 to 11; 0 is January, 1 is February, and so forth; thus 11 is December.
  • A date (day of month) is represented by an integer from 1 to 31 in the usual manner.
  • An hour is represented by an integer from 0 to 23. Thus, the hour from midnight to 1 a.m. is hour 0, and the hour from noon to 1 p.m. is hour 12.
  • A minute is represented by an integer from 0 to 59 in the usual manner.
  • A second is represented by an integer from 0 to 61; the values 60 and 61 occur only for leap seconds and even then only in Java implementations that actually track leap seconds correctly. Because of the manner in which leap seconds are currently introduced, it is extremely unlikely that two leap seconds will occur in the same minute, but this specification follows the date and time conventions for ISO C.

     System.out.println("2 ======================");
     
     // Calendar to Date 1 
     Date curdate2 = curcal1.getTime();
     System.out.println("Calendar to Date M1:"+curdate2);
     
     // Calendar to Date 2
     Date curdate3 = new Date(curcal1.getTimeInMillis());
     System.out.println("Calendar to Date M2:"+curdate3);
     
     // Date to Calendar
     Calendar caldatetest = Calendar.getInstance();
     caldatetest.set(2000, 1/*Month:0-Jan, 1-Feb...*/, 1);
     System.out.println("set Calendar 2000/1/1:"+caldatetest.getTime());
     caldatetest.setTime(curdate2);
     System.out.println("Date to Calendar:"+caldatetest.getTime());

결과


2 ======================
Calendar to Date M1:Sun Apr 30 14:38:26 KST 2017
Calendar to Date M2:Sun Apr 30 14:38:26 KST 2017
set Calendar 2000/1/1:Tue Feb 01 14:38:26 KST 2000
Date to Calendar:Sun Apr 30 14:38:26 KST 2017


date의 시간 설정

setTime()에 메소드에 시간 정보를 적게되는데 결과를 보면 1000 차이가 1초를 나타냅니다. 즉 ms단위로 기록을 해줘야 하며 0 의값은 1970/1/1 의 값을 가짐을 알 수 있습니다. 9시인 이유는 시스템 설정이 GMT+9라서 그렇습니다.

     System.out.println("3 ======================");
     
     // Date toString()
     System.out.println("Date.toString():"+curdate3.toString());
     
     // Date.setTime
     curdate2.setTime(0);
     System.out.println("Date.seTime(0):"+curdate2);
     
     curdate2.setTime(1000);
     System.out.println("Date.seTime(1000):"+curdate2);

결과

3 ======================
Date.toString():Sun Apr 30 14:38:26 KST 2017
Date.seTime(0):Thu Jan 01 09:00:00 KST 1970
Date.seTime(1000):Thu Jan 01 09:00:01 KST 1970


날짜 format

시간을 표현하는데에는 SimpleDateFormat 을 이용하면 원하는 포맷으로 출력하거나 입력 받을 수 있습니다.

     System.out.println("4 ======================");
     
     // Print date Format
     SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     System.out.println("SimpleDateFormat.format:"+format.format(curdate1));
     
     // Load data format
     Date savedDate = new Date(0);
     try {
      savedDate = format.parse("2017-04-17 00:17:29");
  } catch (java.text.ParseException e) {
   e.printStackTrace();
  }
     System.out.println("SimpleDateFormat.parse:"+format.format(savedDate));
     

결과

4 ======================
SimpleDateFormat.format:2017-04-30 14:38:26
SimpleDateFormat.parse:2017-04-17 00:17:29


시간 비교


시간 비교 메소드는 before(), after()를 사용하게 됩니다. 자기 자신이 기준이 되므로 현재시각.before()는 자기 시각이 인자로 주어지는 시각에 비해 앞에 있으면 true가 됩니다.

     System.out.println("5 ======================");
     
     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     Calendar afterDate = Calendar.getInstance();
     Calendar beforeDate = Calendar.getInstance();
     Calendar baseDate = Calendar.getInstance();
     afterDate.set(2000, 2, 3, 4, 5);
     baseDate.set(2000, 1, 2, 3, 4);
     beforeDate.set(2000, 0, 1, 2, 3);
     System.out.println("afterDate:"+dateFormat.format(afterDate.getTime()));
     System.out.println("baseDate:"+dateFormat.format(baseDate.getTime()));
     System.out.println("beforeDate:"+dateFormat.format(beforeDate.getTime()));
     if(baseDate.before(beforeDate)){
      System.out.println("before true:"+dateFormat.format(beforeDate.getTime()));
     }
     if(baseDate.before(afterDate)){
      System.out.println("before true:"+dateFormat.format(afterDate.getTime()));
     }
     if(baseDate.after(beforeDate)){
      System.out.println("after true:"+dateFormat.format(beforeDate.getTime()));
     }
     if(baseDate.after(afterDate)){
      System.out.println("after true:"+dateFormat.format(afterDate.getTime()));
     }

결과

5 ======================
afterDate:2000-03-03 04:05:06
baseDate:2000-02-02 03:04:06
beforeDate:2000-01-01 02:03:06
before true:2000-03-03 04:05:06
after true:2000-01-01 02:03:06








2017년 4월 15일 토요일

Java 텍스트 파일 읽어 라인에 저장하기 출력하기 (reading text file and print text in java)


서론

일반적으로 파일을 읽는 예제들은 많이 있지만, 읽어서 라인 단위로 리턴하는 예제입니다.
용도는 line단위로 미리 정해놓은 용도가 있을때 줄 단위로 읽어서(read) 데이터를 로딩하기 위한 목적입니다.

예) 
날짜
시간

일반적이라면 ini 형태나 xml형태를 하는편이 좋겠지만 딱 이용도로만 사용할 예정이라서 만들어 봤습니다.


실행 코드

package testProject;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class FileLineRead {
 public static List<String> fileLineRead(String name) throws IOException
 {
  List<String> retStr = new ArrayList<String>();
  BufferedReader in = new BufferedReader(new FileReader(name));
  String s;
  while ((s = in.readLine()) != null) {
   retStr.add(s);
  }
  in.close();
  return retStr;  
 }
 
 public static void main(String args[]) throws IOException {
  List<String> ret = fileLineRead("data.txt");
  for(int i = 0;i<ret.size();i++){
   System.out.println("Line("+i+")"+ret.get(i));
  }
 }
}

data.txt의 내용

안녕하세요.1
안녕하세요.2

실행 결과

Line(0)안녕하세요.1
Line(1)안녕하세요.2


타 Project에 적용
적용할곳은 Webcrawler입니다. 하루에 한번만 실행하는 옵션을 넣을 예정입니다.
기본 구현 개념은 시작 시점에 실행되는 시각을 저장했다가 다음번 실행시 읽어서 비교하는 방식입니다.


2017년 4월 8일 토요일

java에서 Zip 압축 사용하기

Java 에서 Zip 압축 사용하기


대부분의 Zip예제들은 파일이 존재하고 존재하는 파일들을 압축하는 예제들입니다.
하지만 여기에서는 존재하는 파일을 압축하는것이 아니라 저장해야 하는 데이터를 압축해서 저장하는 예제입니다.

Zip 예제
package prj.dish;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

// 1. init() 
// 2. add()
// 3. close()

public class MZip {
 FileOutputStream fos = null;
 ZipOutputStream zos = null;

 public void init(String output) throws Throwable{
  try{
   fos = new FileOutputStream(new File(output));
   zos = new ZipOutputStream(fos);

  }catch(Throwable e){
   throw e;
  }
 }

 public void add(String name,byte data[]) throws Throwable{
  ZipEntry ze= new ZipEntry(name);
  zos.putNextEntry(ze);
  zos.write(data);
  zos.closeEntry();
 }

 public void close() throws Throwable{
  try{
   if(zos != null) zos.close();
   if(fos != null) fos.close();
  }catch(Throwable e){
   throw e;
  }
 }

 public static void main(String[] args) throws Throwable {
  MZip mzip = new MZip();
  mzip.init("zipfile.zip");
  mzip.add("A/data1", "11111".getBytes());
  mzip.add("A/data2", "22222".getBytes());
  mzip.close();
 }
}

실행코드 설명
  MZip mzip = new MZip();
  mzip.init("zipfile.zip");
  mzip.add("A/data1", "11111".getBytes());
  mzip.add("A/data2", "22222".getBytes());
  mzip.close();

MZip Class를 만들었습니다. 호출 순서는 init()->add()->close() 순서 입니다.
init()는 생성할 zip파일이름을 인자로 넘겨줍니다.
add()는 zip안에 개별적으로 존재하는 파일명과 데이터를 넘겨줍니다.
close()는 열어 놓은 stream을 닫습니다.

위 main코드를 실행하면 아래와 같은 결과가 나옵니다.

zipfiles.zip 파일이 생성되고 압축을 풀어보면 A폴더가 생기고 data1,data2 파일이 아래와 같습니다.


해당 코드를 이미 작성한 webcrawler에 넣어 봤습니다.

기존에는 그냥 파일만 생성했는데요, 이번에는 생성되는 파일이 zip형태로 압축되어 생성되도록 변경하였습니다.

원본 코드 : http://swlock.blogspot.com/2017/01/web-crawler-with-java.html
원본 설명 : http://swlock.blogspot.com/2017/01/web-crawler-with-java_20.html

webcrawler zip추가된 소스
MZip Class를 추가 해야함
package prj.dish;

import java.io.ByteArrayOutputStream;
import java.io.Console;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.zip.GZIPInputStream;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import net.htmlparser.jericho.Config;
import net.htmlparser.jericho.Element;
import net.htmlparser.jericho.LoggerProvider;
import net.htmlparser.jericho.Source;

// Example : java -jar WebCrawler.jar -u http://finance.daum.net -s E:\webdata
// done : 2017.4.8 출력 파일 zip 압축하기

public class Webcrawler {
 public static boolean SUPPORT_MZIP = true;

 private MZip mzip = null;
 private int maxDepth = 1;
 private int maxHostChange = 1;
 private String savePath;
 private String host;
 boolean DOMAIN_CHANGE = true;
 byte[] htmlByte = null;
 HashSet<String> visited = new HashSet<String>();
 CloseableHttpClient httpclient = HttpClients.createDefault();


 public static void main(String[] args) {
  System.out.println("Welcome !! Webcrawler");
  Config.LoggerProvider=LoggerProvider.DISABLED;
  System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.NoOpLog"); 
  if(args.length >= 1){
   Options options = new Options();

   Option savepath = new Option("s", "savepath", true, "input save folder file path");
   savepath.setRequired(true);
   options.addOption(savepath);

   Option url = new Option("u", "url", true, "url ex) http://www.daum.net");
   url.setRequired(true);
   options.addOption(url);

   Option depth = new Option("d", "depth", true, "max depth");
   depth.setRequired(false);
   options.addOption(depth);

   Option changehostdepth = new Option("c", "changehostdepth", true, "change host depth");
   changehostdepth.setRequired(false);
   options.addOption(changehostdepth);

   CommandLineParser parser = new DefaultParser();
   HelpFormatter formatter = new HelpFormatter();
   CommandLine cmd;

   try {
    cmd = parser.parse(options, args);
   } catch (ParseException e) {
    System.out.println(e.getMessage());
    formatter.printHelp("Webcrawler", options);
    System.exit(1);
    return;
   }

   String saveFilePath = cmd.getOptionValue("savepath");
   String urlPath = cmd.getOptionValue("url");
   String depthParam = cmd.getOptionValue("depth");
   if(depthParam==null || depthParam.isEmpty()) depthParam = "2";
   String changehostdepthdepthParam = cmd.getOptionValue("changehostdepth");
   if(changehostdepthdepthParam==null || changehostdepthdepthParam.isEmpty()) changehostdepthdepthParam = "1";
   System.out.println(urlPath);
   Webcrawler crawler;
   crawler = new Webcrawler();
   crawler.setSavePath(saveFilePath);
   crawler.setMaxDepth(Integer.valueOf(depthParam));
   crawler.setMaxHostChange(Integer.valueOf(changehostdepthdepthParam));
   crawler.run(urlPath);
  }
  System.out.println("End Webcrawler");
 }

 private void run(String string) {
  host = string;
  connect( host, "/", 0, 0);

  // MZip
  if( SUPPORT_MZIP ){
   try {
    mzip.close();
   } catch (Throwable e) {
    e.printStackTrace();
    exitWait();
   }
  }
 }
 private void exitWait() {
  Console console = System.console();
  console.readLine();
 }

 public String getString() {
  try {
   return new String(htmlByte, "UTF-8");
  } catch (UnsupportedEncodingException e) {
   e.printStackTrace();
  }
  return null;
 }
 private String getHttp(String url) throws IOException, URISyntaxException{
  String ret=null;
  try {
   HttpGet httpGet = new HttpGet(url);
   HttpClientContext context = HttpClientContext.create();
   httpGet.setHeader(HttpHeaders.ACCEPT_ENCODING, "gzip");
   CloseableHttpResponse response = httpclient.execute(httpGet,context);
   try {
    System.out.println(response.getStatusLine());
    HttpEntity entity = response.getEntity();

    Header contentEncoding = response.getFirstHeader("Content-Encoding");
    if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
     System.out.println("gziped");
     htmlByte = inputStreamToByte( new GZIPInputStream(entity.getContent()));
    }else {
     htmlByte = inputStreamToByte(entity.getContent());
    }

    HttpHost target = context.getTargetHost();
    List<URI> redirectLocations = context.getRedirectLocations();
    URI location = URIUtils.resolve(httpGet.getURI(), target, redirectLocations);
    System.out.println("Final HTTP location: " + location.toASCIIString());
    ret = location.toASCIIString();
   } finally {
    response.close();
   }
  } finally {
   //httpclient.close();
  }
  return ret;
 }
 private byte[] inputStreamToByte(InputStream in)
 {
  final int BUF_SIZE = 1024;
  ByteArrayOutputStream out = new ByteArrayOutputStream();
  byte[] buffer = new byte[BUF_SIZE];
  try {
   int length;
   while ((length = in.read(buffer)) != -1) out.write(buffer, 0, length);
  } catch (IOException e) {
   e.printStackTrace();
   return null;
  }
  return out.toByteArray();
 }
 private void connect(String lasturl, String addurl, int depth, int hostchange) {
  Source source = null;
  String newurl = null;
  int hostchanged = 0;

  if(addurl.startsWith("http://") || addurl.startsWith("https://")){
   hostchanged = 1;
   if( maxHostChange <= hostchange+hostchanged) return;
  }
  if( maxDepth <= depth ){
   return;
  }
  try {
   //if(DOMAIN_CHANGE){
   lasturl = calcNextUrl(lasturl, addurl);
   //}else{
   //lasturl = urlChg(host, lasturl, addurl);
   //}
   System.out.println("Get:["+depth+"]:"+lasturl);
   if( !visited.contains(lasturl) ){
    visited.add(lasturl);
   }else{
    System.out.println("visited !");
    return;
   }
   //source=new Source(new URL(lasturl));
   newurl = getHttp(lasturl);
   //fileSave(savePath + changeFileName(lasturl)+".htm",getString());

   // MZip
   if( SUPPORT_MZIP ){
    try {
     mzip.add(changeFileName(lasturl)+".htm", htmlByte);
    } catch (Throwable e) {
     e.printStackTrace();
    }
   }else{
    fileSave(savePath + changeFileName(lasturl)+".htm",htmlByte);
   }

   source=new Source(getString());

  } catch (Exception e) {
   e.printStackTrace();
   return;
  }
  //System.out.println(source.getRenderer().toString());
  List <Element> elements = source.getAllElements("a");
  System.out.println("Len:("+htmlByte.length+"), A tag("+elements.size()+")");
  for(int i = 0 ; i < elements.size(); i++){
   Element ele = elements.get(i);
   String href = ele.getAttributeValue("href");
   if(href==null || href.isEmpty()) continue;
   if(!DOMAIN_CHANGE){
    if(href.startsWith("http://") || href.startsWith("https://")){
     continue;
    }
   }
   if(href.startsWith("javascript:")){
    continue;
   }else if(href.contains("#")){
    continue;
   }else if(href.startsWith("<")){
    continue;
   }
   connect(newurl,href,depth+1,hostchange+hostchanged);
  }
 }

 private void fileSave(String name, byte[] htmlByte) {
  FileOutputStream stream = null;
  try{
   stream = new FileOutputStream(name);
   stream.write(htmlByte);
  } catch (Exception e) {
  } finally {
   try {
    stream.close();
   } catch (IOException e) {
   }
  }
 }

 private String changeFileName(String lasturl) {
  lasturl=lasturl.replace('?', '_');
  lasturl=lasturl.replace('*', '_');
  lasturl=lasturl.replace('%', '_');
  lasturl=lasturl.replace('.', '_');
  lasturl=lasturl.replace('/', '_');
  lasturl=lasturl.replace('\\', '_');
  lasturl=lasturl.replace('\"', '_');
  lasturl=lasturl.replace('\'', '_');
  lasturl=lasturl.replace('|', '_');
  lasturl=lasturl.replace('+', '_');
  lasturl=lasturl.replace('-', '_');
  lasturl=lasturl.replace(':', '_');
  return lasturl;
 }

 private void setMaxDepth(int i) {
  maxDepth = i;
 }

 private void setMaxHostChange(int i) {
  maxHostChange = i;
 }

 private void setSavePath(String string) {
  savePath = string;
  if(!savePath.endsWith("/")) savePath=savePath+"/";
  createDirectoryIfNeeded(string);
  String timeStamp = new SimpleDateFormat("yyyy.MM.dd.HH.mm").format(new Date());
  savePath=savePath+timeStamp;
  if( SUPPORT_MZIP ) {
   mzip = new MZip();
   try {
    mzip.init(savePath+".zip");
   } catch (Throwable e) {
    e.printStackTrace();
   }
  }else{
   createDirectoryIfNeeded(savePath);
   if(!savePath.endsWith("/")) savePath=savePath+"/";
  }
 }

 private void createDirectoryIfNeeded(String directoryName)
 {
  File theDir = new File(directoryName); 
  if (!theDir.exists())
   theDir.mkdirs();
 }

 private void Webcrawler() {
 }

 public static String calcNextUrl(String thisurl, String add)
 {
  System.out.println("This:["+thisurl + "]Add:["+add+"]");
  URI thisuri = URI.create(thisurl);
  String data = thisuri.getScheme() + "://" + thisuri.getHost();
  if(thisuri.getPort()!=-1) data=":"+thisuri.getPort();
  if(add.startsWith("/")) data=data+add;
  else if(add.startsWith("http")) data=add;
  else {
   data=thisurl;
   if(data.endsWith("/")) data=data+add;
   else data=data+"/"+add;
  }

  URI returi = URI.create(data);
  returi = returi.normalize();
  if( !returi.toString().startsWith("http") ){
   System.out.println("Error");
  }
  return returi.toString();
 }

 public static void fileSave(String name,String data)
 {
  try {
   File newTextFile = new File(name);
   FileWriter fw = new FileWriter(newTextFile);
   fw.write(data);
   fw.close();
  } catch (IOException iox) {
   iox.printStackTrace();
  }
 }
}

기존 설명 대비 추가되는 코드는 SUPPORT_MZIP 로 검색하면 추가된 내용만 볼 수 있습니다. 

테스트 실행

테스트 실행시 사용한 인자
-u http://www.daum.net -s save -d 2 -c 1