[Java] Thumbnail Image 만드는 코드

코드 정리하다가 블로그에 정리되지 않은 코드가 있어 정리해 봅니다. 일반적인 이미지를 작은 이미지, 즉 썸네일 이미지로 만들어 주는 코드입니다. 이미지의 종횡비 크기를 유지하며 썸네일 이미지를 만들어 줍니다. 썸네일 이미지 만드는 코드를 실제 프로젝트에 붙이기 위해 만들다 보니, 부가적인 내용이 덧붙여 있으니 참고하시기 바랍니다.

사용은 아래처럼 하면 됩니다. 즉, d:/a.jpg 파일을 썸네일 이미지를 만드는데, 가로 또는 세로 크기 중 하나를 256을 만듭니다.

reateThumbnail("d:/a.jpg", 256);

아래는 d:/a.jpg의 실제 이미지입니다. 이미지 크기는 920×600입니다.

위의 이미지가 위의 코드를 통해 아래와 같이 d:/a.thumbnail.jpg 파일로 만들어지고 크기는 256×166이 됩니다. 원본 이미지의 가로 크기가 세로보다 길다보니 가로를 256으로 만들어지게 되는 것입니다.

createThumbnail 함수는 아래와 같습니다.

private static boolean createThumbnail(String fileName, int maxSize) {
    try {
        int thumbnail_width = maxSize;
        int thumbnail_height = maxSize;

        File origin_file_name = new File(fileName);
		    
        String ext = getFileExt(fileName);
        String newFileName = fileName.replace("." + ext, ".thumbnail." + ext);
		    
        BufferedImage buffer_original_image = ImageIO.read(origin_file_name);
		    
        double imgWidth = buffer_original_image.getWidth();
        double imgHeight = buffer_original_image.getHeight();
		    
        if(imgWidth < imgHeight) {
            thumbnail_width = (int)((imgWidth / imgHeight) * maxSize);
        } else {
            thumbnail_height = (int)((imgHeight / imgWidth) * maxSize);
        }
		    
        int imgType = (buffer_original_image.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
        BufferedImage buffer_thumbnail_image = new BufferedImage(thumbnail_width, thumbnail_height, imgType);
        Graphics2D graphic = buffer_thumbnail_image.createGraphics();
		    
        graphic.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        graphic.drawImage(buffer_original_image, 0, 0, thumbnail_width, thumbnail_height, null);
		    
        if(ext.equalsIgnoreCase("jpg")) {
            writeJpeg(buffer_thumbnail_image, newFileName, 1.0f);
        } else {
            File thumb_file_name = new File(newFileName);
            ImageIO.write(buffer_thumbnail_image, ext.toLowerCase(), thumb_file_name);
        }
		    
        graphic.dispose();
    } catch (Exception e) {
        e.printStackTrace(System.err);
        return false;
    }
		
    return true;
}

위의 함수는 2개의 내부 함수를 호출하고 있는데, 해당되는 함수들은 아래와 같습니다.

private static String getFileExt(String fileName) { // "abc.txt" -> "txt", not ".txt"
    int i = fileName.lastIndexOf('.');
    int p = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));

    if (i > p) {
        return fileName.substring(i+1);
    }

    return null;
}
	 
private static void writeJpeg(BufferedImage image, String destFile, float quality) throws IOException {
    ImageWriter writer = null;
    FileImageOutputStream output = null;
		  
    try {
        writer = ImageIO.getImageWritersByFormatName("jpeg").next();
	
        ImageWriteParam param = writer.getDefaultWriteParam();
		    
        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        param.setCompressionQuality(quality);
		    
        output = new FileImageOutputStream(new File(destFile));
        writer.setOutput(output);
		    
        IIOImage iioImage = new IIOImage(image, null, null);
        writer.write(null, iioImage, param);
    } catch (IOException ex) {
        throw ex;
    } finally {
        if (writer != null) {
            writer.dispose();
        }
		    
        if (output != null) {
            output.close();
        }
    }
}

단위 테스트로 만들어 놓은 코드인지라 최적화가 되어 있지 않으니, 살펴보시고 최적화 해 사용하시기 바랍니다.

웹에서 Javascript 만으로 텍스트 파일 읽기

웹에서 JS 언어만으로 로컬에 저장된 텍스트 파일을 읽어 오는 코드를 정리한 글이다. 먼저 아래는 예제 코드 실행을 위한 DOM 구성에 대한 코드이다.


...

Open 버튼을 클릭하면 텍스트 파일을 선택할 수 있는 대화상자가 표시되도록 하며, 여기서 읽고자 하는 파일을 사용자가 선택하면 id가 output인 div에 텍스트 파일의 내용을 출력한다. 이에 대한 코드는 아래와 같다.

function openTextFile() {
    var input = document.createElement("input");

    input.type = "file";
    input.accept = "text/plain"; // 확장자가 xxx, yyy 일때, ".xxx, .yyy"

    input.onchange = function (event) {
        processFile(event.target.files[0]);
    };

    input.click();
}

function processFile(file) {
    var reader = new FileReader();

    reader.onload = function () {
        output.innerText = reader.result;
    };

    reader.readAsText(file, /* optional */ "euc-kr");
}

openTextFile은 Open 버튼 클릭시 호출하는 함수이다. processFile은 openTextFile에서 호출되는 함수로 선택된 파일을 읽어 div에 그 내용을 출력한다. IE와 Chrome 모두에서 정상적으로 작동하는 것을 확인했다.

웹에서 Javascript 만으로 텍스트 파일 생성

웹에서 스크립트만으로 텍스트 파일을 생성하기 위한 코드를 정리한다. 주로 사용하는 웹브라우져가 IE와 Chrome인데, 텍스트 파일을 생성하는 방식이 서로 다르다. 먼저 크롬의 경우에는 아래와 같다.

function saveToFile_Chrome(fileName, content) {
    var blob = new Blob([content], { type: 'text/plain' });

    objURL = window.URL.createObjectURL(blob);
            
    // 이전에 생성된 메모리 해제
    if (window.__Xr_objURL_forCreatingFile__) {
        window.URL.revokeObjectURL(window.__Xr_objURL_forCreatingFile__);
    }
    window.__Xr_objURL_forCreatingFile__ = objURL;

    var a = document.createElement('a');

    a.download = fileName;
    a.href = objURL;
    a.click();
}

다음은 IE에서 작동하는 코드이다.

function saveToFile_IE(fileName, content) {
    var blob = new Blob([content], { type: "text/plain", endings: "native" });

    window.navigator.msSaveBlob(blob, fileName);
    //window.navigator.msSaveOrOpenBlob(blob, fileName);
}

아래는 현재 사용하는 웹브라우저가 IE인지를 식별하는 함수이다.

function isIE() {
    return (navigator.appName === 'Netscape' && navigator.userAgent.search('Trident') !== -1) ||
        navigator.userAgent.toLowerCase().indexOf("msie") !== -1;
}

[Java] 원하는 시간에 원하는 기능을 실행해 주는 Scheduler 라이브러리, Quartz v2.2.3

원하는 시간에 원하는 기능을 실행해 주는 스케줄러 기능을 Java에서 실행하기 위해서 가장 많이 사용되는 라이브러리가 Quartz입니다. Quartz는 기능을 안정적으로 실행할 수 있다는 장점과 함께, 특히 실행할 시간을 매우 유연하게 지정할 수 있습니다.

아래의 코드는 매 순간의 시간이 5초일때(5초마다가 아님) 특정한 기능, 즉 Job을 실행해 주는 코드입니다.

package quartz;

import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

public class MainEntry {
    public static void main(String[] args) {
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
		
        try {
            Scheduler scheduler = schedulerFactory.getScheduler();
			
            JobDetail job = newJob(TestJob.class)
                .withIdentity("jobName", Scheduler.DEFAULT_GROUP)
                .build();
			
            Trigger trigger = newTrigger()
                .withIdentity("trggerName", Scheduler.DEFAULT_GROUP)
                .withSchedule(cronSchedule("5 * * * * ?"))
                .build();
						
            scheduler.scheduleJob(job, trigger);
            scheduler.start();
        } catch(Exception e) {
            e.printStackTrace();
        }		
    }
}

26번째 코드를 보면 cronSchedule 함수를 통해 Job을 실행할 시간을 정하고 있습니다. 시간을 정하는 방식을 5 * * * * ?인데요. 순서대로 초 분 시 일 월 요일 년도(옵션)입니다. 즉 초를 5로 하고 나머지에 대해서는 * 또는 ?로 지정함으로써 매순간의 시간이 5초일때 Job을 실행하게 됩니다. 그럼, 실행할 Job은 어떻게 지정할까요? 20번 코드의 newJob 함수를 통해 지정하고 있습니다. Job으로 TestJob을 사용하고 있는데.. 이 TestJob의 코드는 아래와 같습니다.

package quartz;

import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class TestJob implements Job {
    @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        System.out.println("Job Executed [" + new Date(System.currentTimeMillis()) + "]"); 
    }
}

위의 예에서는 간단히 Job이 실행되는 시간과 Job Executed라는 문자열을 표시하는 일만 합니다.

Quartz 라이브러리는 http://www.quartz-scheduler.org/downloads 에서 받을 수 있으며, 다운로드 받은 jar 중 최소한 quartz-{version}.jar와 slf4j-api-{version}.jar를 참조해줘야 합니다.

회전 제한 및 양방향 성질을 가진 네트워크 DB를 활용한 A* 알고리즘

실제 도로망은 U턴 제한이나 우회전 제한 등과 같은 회전 제한에 대한 성질과 좌측 및 우측 차로에 대한 방향에 대한 성질을 가집니다. 이러한 성질에 대한 속성값을 가지는 네트워크 데이터는 지능형 교통체계 표준 노드링크 관리시스템(http://nodelink.its.go.kr)에서 제공하고 있습니다.

이 글은 지능형 교통체계 표준 노드링크 관리시스템에서 제공하는 네트워크 DB에 대해 A* 알고리즘을 적용하는 각 단계별 과정에 대한 상태정보를 기록한 자료에 대한 글입니다. A*에 대한 자세한 설명은 기존의 최단 경로 탐색 – A* 알고리즘이라는 글을 참고하시기 바랍니다. 본 글은 A* 알고리즘을 확장하고 응용한 글이므로 반드시 A* 알고리즘을 완전하게 이해하고 있는 상태에서만 의미있게 이해될 수 있는 글이라는 점을 알려드립니다.

해결하고자 하는 네트워크 DB에서의 최적경로에 대한 문제는 아래 그림과 같습니다.

위의 문제에 대해 확장된 A* 알고리즘을 통해 최종적으로 도출된 그 결과는 아래의 그림과 같습니다.

해결하고자 하는 문제와 그 결과 도출을 위한 알고리즘의 각 단계를 정리한 내용은 아래의 pdf 파일에 담겨 있습니다.