2010年11月1日月曜日

GrailsとTika

アップロードしたファイルのテキストを抽出したいのでApache Tikaを使う。

grails-app/conf/BuildConfig.groovy

grails.project.dependency.resolution = {
dependencies {
build 'org.apache.tika:tika-parsers:0.7'
}
}


grails-app/controller/UploadFileController.groovy

import org.springframework.web.multipart.MultipartFile
import org.apache.tika.metadata.Metadata
import org.apache.tika.parser.AutoDetectParser
import org.apache.tika.sax.BodyContentHandler

class UploadFileController {

:

def save = {
def uploadFileInstance = new UploadFile()
def file = params.file
if (file instanceof MultipartFile && file.originalFilename) {
uploadFileInstance.name = file.originalFilename
uploadFileInstance.size = file.size
uploadFileInstance.contentType = file.contentType
uploadFileInstance.bytes = file.bytes
def stream
try {
stream = file.inputStream
def parser = new AutoDetectParser()
def writer = new StringWriter()
def handler = new BodyContentHandler(writer)
def metadata = new Metadata()
parser.parse(stream, handler, metadata)
uploadFileInstance.text = writer.toString()
}
catch (Exception e) {
log.warn(e.message, e)
}
finally {
stream?.close()
}
}
if (uploadFileInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'uploadFile.label', default: 'UploadFile'), uploadFileInstance.id])}"
redirect(action: "show", id: uploadFileInstance.id)
}
else {
render(view: "create", model: [uploadFileInstance: uploadFileInstance])
}
}

:
}

2010年10月28日木曜日

Grailsでファイルアップロード

こんなドメインクラスを作ってファイルをアップロードする。


class UploadFile {

String name
Long size
String contentType
byte[] bytes

static constraints = {
}
}


これだと一覧表示でUploadFile.list()すると、不要にファイルの内容まで引っ張ってきてしまって遅い。なのでこうする。


class UploadFile {

String name
Long size
String contentType
UploadData data

static constraints = {
}
}

class UploadData {

byte[] bytes

static belongsTo = [UploadFile]

static constraints = {
}
}


これだとUploadDataへの関連がデフォルトでlazy:trueなので、一覧表示が速くなる。DataBindingはできなくなるけど仕方ない。他にいい方法ないかなぁ。

2010年9月3日金曜日

launch4jを修正2

launch4jを使って実行ファイル作ったのはいいけど、C++からキックしたら標準出力などが引き継がれてなかったのでちょっと修正した。ただこれで正しいかは微妙。あとコマンドプロンプトが表示されてしまったので表示しないように修正した。


launch4j/head_src/head.c

DWORD execute(const BOOL wait) {
STARTUPINFO si;
memset(&pi, 0, sizeof(pi));
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);

// ここから5行追加
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;

2010年9月2日木曜日

launch4jを修正

作成されるexeファイルのプロパティを見ると、バージョン情報タブに「言語」という項目があって、これが「英語(米国)」と表示されてしまう。これを「日本語」にしたい。

どうも設定する箇所がないのでソースコードを見たら、コメントにEnglishってあって、ハードコーディングされていたので、RcBuilder.javaを修正する。修正は2箇所。


182:BLOCK \"041103A4\"\n" + // English
:
192:_sb.append(" }\n }\nBLOCK \"VarFileInfo\"\n{\nVALUE \"Translation\", 0x0411, 0x03A4\n}\n}");

2010年8月16日月曜日

文字列を実行するための覚え書き

import javax.script.*
def engine = new ScriptEngineManager().getEngineByName("groovy");
engine.put('arg', false)
def result = engine.eval("println 'hello'; arg")
println result

指定できる名前
println new ScriptEngineManager().getEngineFactories()*.getNames().flatten()
[groovy, Groovy, js, rhino, JavaScript, javascript, ECMAScript, ecmascript]

grailsのメッセージファイル(自分用)

1.3.x用に更新

default.doesnt.match.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]パターンと一致していません。
default.invalid.url.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、URLではありません。
default.invalid.creditCard.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、正当なクレジットカード番号ではありません。
default.invalid.email.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、メールアドレスではありません。
default.invalid.range.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]から[{4}]範囲内を指定してください。
default.invalid.size.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]から[{4}]以内を指定してください。
default.invalid.max.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最大値[{3}]より大きいです。
default.invalid.min.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最小値[{3}]より小さいです。
default.invalid.max.size.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最大値[{3}]より大きいです。
default.invalid.min.size.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最小値[{3}]より小さいです。
default.invalid.validator.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、カスタムバリデーションを通過できません。
default.not.inlist.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]リスト内に存在しません。
default.blank.message=[{1}]クラスのプロパティ[{0}]の空白は許可されません。
default.not.equal.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]と同等ではありません。
default.null.message=[{1}]クラスのプロパティ[{0}]にnullは許可されません。
default.not.unique.message=クラス[{1}]プロパティ[{0}]の値[{2}]は既に使用されています。

default.paginate.prev=戻る
default.paginate.next=次へ
default.boolean.true=はい
default.boolean.false=いいえ
default.date.format=yyyy/MM/dd
default.number.format=0

default.created.message={0}{1}を作成しました。
default.updated.message={0}{1}を更新しました。
default.deleted.message={0}{1}を削除しました。
default.not.deleted.message={0}{1}を削除できませんでした。
default.not.found.message={0}{1}が見つかりません。
default.optimistic.locking.failure={0}{1}は他のユーザによって更新されました。

default.home.label=ホーム
default.list.label={0}一覧
default.add.label={0}追加
default.new.label=新規{0}
default.create.label={0}作成
default.show.label={0}詳細
default.edit.label={0}編集

default.button.create.label=作成
default.button.edit.label=編集
default.button.update.label=更新
default.button.delete.label=削除
default.button.delete.confirm.message=本当に削除しますか?

# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
typeMismatch.java.net.URL=プロパティ[{0}]には有効なURLを入力してください。
typeMismatch.java.net.URI=プロパティ[{0}]には有効なURIを入力してください。
typeMismatch.java.util.Date=プロパティ[{0}]には有効な日時を入力してください。
typeMismatch.java.lang.Double=プロパティ[{0}]には有効な数値を入力してください。
typeMismatch.java.lang.Integer=プロパティ[{0}]には有効な数値を入力してください。
typeMismatch.java.lang.Long=プロパティ[{0}]には有効な数値を入力してください。
typeMismatch.java.lang.Short=プロパティ[{0}]には有効な数値を入力してください。
typeMismatch.java.math.BigDecimal=プロパティ[{0}]には有効な数値を入力してください。
typeMismatch.java.math.BigInteger=プロパティ[{0}]には有効な数値を入力してください。

2010年4月9日金曜日

jQueryのFullCalendarが素敵

FullCalendar
http://arshaw.com/fullcalendar/

Google Calendarっぽいカレンダーが簡単に作れる。

クリックしてイベントを追加できなかったので、追加できるようにしてみた。あと、イベントクリック時のアクションもドキュメントにあったので試してみた。

修正したのはexamples/agenda-views.html

$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
events : [
イベントたち
]
// 以下を追加
,
// カレンダーのどこかをクリックしたらイベントを作る
dayClick: function(date, allDay, jsEvent, view) {
$('#calendar').fullCalendar('addEventSource', [{
title: 'no title',
start: date,
allDay: allDay
}]);
},
// イベントをクリックしたらタイトルをhogeに変える
eventClick: function(calEvent, jsEvent, view) {
calEvent.title = 'hoge';
$('#calendar').fullCalendar('updateEvent', calEvent);
}
}



とりあえず、これはかなりよさげ。