Blob 이미지를 SQLite에 저장하고 불러오기

Blob 객체란?

타이타늄 모바일에서 Blob 객체는 내부 데이터를 바이너리로 가지고 있는 객체를 의미한다.

The blob is an abstract data type that represents binary information, often obtained through HTTPClient or via files. It often is used to store text or the actual data of an image.

그리고 종종 이 객체를 그대로 데이터베이스에 저장하고 싶을때가 있다. 예를 들면 카메라로 찍은 이미지를 서버에 저장하지 않고, 그냥 앱안에 저장하고 싶은 경우가  있을수 있다. 이런경우엔, 보통 2가지를 생각해볼 수있다.

  1. SQLite 를 이용해 이미지를 앱 안에 저장한다.
  2. 찍은 이미지를 로컬 파일 시스템에 저장하고, Native Path를 DB에 저장한다.

여기서 다룰 내용은 바로 1번에 해당하는 내용이다. 그럼, 본론으로 들어가자!

Blob 이미지와 SQLite

타이타늄 모바일 커뮤니티 Q&A 게시판을 뒤져보면, Blob 객체를 SQLite DB에 저장하고 싶어하는 사람들이 많다는 사실을 알수있다. 하지만 그 어떤 누구도 뾰족한 해답을 제시해주지 못하고 있다.

SQLite에 쓰려면 직렬화 하세요!

문제의 핵심은 바로 SQLite DB에 있다. SQLite 가 지원하는 데이터 타입에 분명 BLOB 타입이 존재하지만, 자바스크립트에서 쓰는 그 Blob 객체가 그대로 저장된다는 의미는 아니다. 결국, DB에 데이터를 쓰려면 객체를 직렬화해야한다. 가장 간단한 방법은 객체를 문자열로 만들면 된다. 그럼, Ti.Blob 객체가 제공하는 toString() 메소드를 이용하면 되겠네? 라고 생각할수도 있지만, 결론은 안된다. 그럼 도대체 어쩌란 말인가???

바이너리 객체를 인코딩하세요!

결론은 base64로 인코딩했다가, 디코딩해서 쓰면된다.  코드로 써보면 다음과 같다.

// 저장할땐, 인코딩
exports.setImage = function(_rowId, _blob) {
    var mydb = Ti.Database.open(DATABASE_NAME);
    var sImg = Ti.Utils.base64encode(_blob);
    mydb.execute('UPDATE [table_name] SET blobImg="'+ sImg +'" WHERE  rowId="'+_rowId+'"');
    mydb.close();
};

// 사용할때, 디코딩
var db = require('db');
var encodedImg = db.getImage(rowId);
var blobImg = Ti.Utils.base64decode(encodedImg);

Summary

  1. save Blob image to SQLite

    var sImg = Ti.Utils.base64encode(_blob);

  2. use Blob image from SQLite

    var encodedImg = db.getImage(rowId); var blobImg = Ti.Utils.base64decode(encodedImg) ;

  3. but it is too slow in titanium, don’t use it!

덧,

몇가지 테스트해서 구현해봤더니 이미지를 DB에 때려 박는 무식한 방법은 쓰지 않는게 좋겠다. 이미지를 Base64로 인코딩하고 디코딩하는데 시간이 너무 오래 걸린다. 여튼 위 방법은 속도가 엄청 느리므로 비추한다.

멀티 스레드로 윈도우 열기

웹브라우저에서 동작하는 자바스크립트는 싱글 스레드로 동작한다. 
즉,  어떤 코드를 실행하던간에 순차적으로 실행되고, 이 순서에 따라 의존성도 생긴다.

하지만 타이타늄 모바일의 경우 두가지 방법으로 윈도우를 열수있다.

1. 싱글 스레드로 윈도우 열기 

[app.js]

var parentWin = Ti.UI.createWindow();
var button = Ti.UI.createButton();
button.addEventListener(“click”, function(e){
var childWin = Ti.UI.createWindow();
childWin.open();
});
parentWin.add(button);
parentWin.open();

위와 같이 같은 파일 안에서 윈도우를 정의할 경우 같은 스레드에서 윈도우가 열린다.

2. 멀티 스레드로 윈도우 열기 

멀티 스레드로 윈도우를 열려면, 다음과 같이 윈도우 생성시 URL 속성을 지정한다.

[app.js]

var parentWin = Ti.UI.createWindow();
var button = Ti.UI.createButton();
button.addEventListener(“click”, function(e){
var childWin = Ti.UI.createWindow({ url: “./childWindow.js” });
childWin.open();

Ti.App.fireEvent(“fooEvent”, {data:”message”});
});
parentWin.add(button);
parentWin.open();

[childWindow.js]

var win = Ti.UI.currentWindow;

Ti.App.addEventListener(“fooEvent”, function(e){
Ti.API.info(e.data + ” got it!”);
});

위와 같이 url로 지정하면 childWindow.js 파일 안에 정의된 내용은 다른 스레드로 동작한다.
따라서, 두 윈도우 사이에 데이터를 전송하려면 약간의 시차가 생길수 있다.

예를 들어, 자식 윈도우를 열고
1. childWindow.open()

바로 윈도우로 메시지를 날리면,
2.  Ti.App.fireEvent(“fooEvent”, {data:”message”});

자식 윈도우에서 메시지를 받아야하는데 받지 못한다.
3.  Ti.App.addEventListener(“fooEvent”, function(e){  Ti.API.info(e.data + ” got it!”);  });

따라서 URL을 이용해 윈도우를 열 경우엔, 스레드 생성시간만큼의 약간의 딜레이를 주어야한다.

var childWin = Ti.UI.createWindow({ url: “./childWindow.js” });

childWin.open();
setTimeout(function(){

      Ti.App.fireEvent(“fooEvent”, {data:”message”});

   }, 100); 

});