IE 10과 win8 이 나오면서 flash player 로 된 swfupload 나
activeX upload 에 대한 대안으로 html5 fileupload 방법을 찾게되었고
추가적으로 jQuery-File-Upload 를 찾게 되었다.
HTML5 를 이용한 파일 업로드의 경우 아래 블로그에 아주 자세히 나와있다.
html5-file-upload-with-progress
위 블로그에서 설명한 기능은
html5 progress event 를 이용한 % 숫자로 진행상태 보여주기
XMLHttpRequest 를 이용한 업로드
FormData 를 이용한 fileSize, fileType 출력 이다.
영어로 되어 있지만 소스 코드만 붙여넣기 하더라도 동작할 정도의 소스기 때문에 이해하는데는 어렵지 않다.
fileUpload 받는 서버의 경우는 기존 포스트에 node-fileupload 소스를 간단히 수정해서 쓸수도 있고
xamp 설치후 php로 간단히 파일을 받는 서버를 구현할수도 있다. (file-upload.post-method)
추가적으로 이 포스트에서는 다음과 같은 기능을 구현 및 변경했다.
1. 파일 선택 버튼은 이미지로 변경
2. 파일 선택시 목록에 들어가고 submit 버튼을 이용해 파일리스트를 전송
3. 총 파일 크기 제한
위 기능은 swfupload에서 기본?으로 제공하는 기능으로
</pre>
$(document).ready(function() {
var swfu = new SWFUpload({
'flash_url' : '/resource/swf/swfupload.swf'
, 'file_post_name' : 'uploadedFile'
//, 'post_params' : {'name': 't.png'}
, 'upload_url' : '/upload/exec'
, 'file_types' : '*.*'
, 'file_types_description' : 'allFile'
, 'file_size_limit' : maxFileSize
, 'file_queue_error_handler' : uploadHandleErrors
, 'upload_error_handler' : uploadHandleErrors
, 'file_queued_handler' : uploadQueued
, 'upload_success_handler' : uploadComplete
, 'button_placeholder_id' : 'fileToUpload'
, 'button_image_url' : '/resource/img/browse_bg.gif'
, 'button_text' : '<a href="#" onclick="return false;">' + '파일찾기' + '</a>'
, 'button_text_style' : ' a { fontFamily:gulim,arial; } '
, 'button_text_left_padding' : 6
, 'button_text_top_padding' : 2
, 'button_width' : 66
, 'button_height' : 22
, 'button_action' : SWFUpload.BUTTON_ACTION.SELECT_FILE
, 'button_disable' : false
});
$('#btnSubmit').click(function() {
swfu.startUpload();
});
function uploadQueued( fileObj ) {
$('#uploadFileName').append('<li>' + fileObj.name + '</li>');
}
function uploadHandleErrors( fileObj , error_code , message ) {
switch( error_code )
{
default: return;
case SWFUpload.ERROR_CODE_QUEUE_LIMIT_EXCEEDED: alert( message ); break;
case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE: alert( 'FILE_INCORRECT' ); break;
case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT: alert('FILE_SIZE_TOO_BIG'); break;
case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE: alert('NOT_CSV_FILE'); break;
case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED: alert( message ); break;
case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED: alert( message ); break;
}
}
function uploadComplete( fileObj , data ) {
var result = $.parseJSON(data);
if( result.msg == '[SUCCESS]' ) {
$( '#uploadFileName' ).html('');
alert('registComplete');
//swfu = null;
return;
}
else {
//swfu = null;
alert('registFail');
}
}
<pre>});
대충 저런식으로 처리를 해주면 된다.
HTML5 를 이용하는 방법은…
</pre>
<form id="form3" enctype="multipart/form-data" method="post" action="">
<div class="row">
<input type="hidden" name="MAX_FILE_SIZE" value="3000000" />
<label for="fileToUpload3">HTML5 Select a File to Upload</label><br />
<img src="/resource/img/button.png" onclick="document.getElementById('fileToUpload3').click();" />
<input type="file" size="30" name="uploadedFile" id="fileToUpload3" style="display:none;"/>
<input type="text" name="userId" id="textTest3" value="111" />
</div>
<div><ul id='uploadFileName3'></ul></div>
<div id="fileSize3"></div>
<div id="fileType3"></div>
<div class="row">
<input type="button" value="Upload" id="btnSubmit3"/>
</div>
<div id="progressNumber3"></div>
</form>
위와 같이 우선 버튼을 이미지로 바꾼 form 을 선언하고
</pre>
$(document).ready(function() {
var fd = new FormData();
$('form:#form3').change(function() {
fileSelected('fileToUpload3', fd);
});
$('#btnSubmit3').click(function() {
uploadFile(fd);
});
function fileSelected(sInputId, fd) {
var file = document.getElementById(sInputId).files[0];
if (file) {
var fileSize = 0;
if (!fd.curFileSize) {
fd.curFileSize = 0;
}
fd.curFileSize += file.size;
if (fd.curFileSize > maxFileSize) {
alert('FILE_SIZE_TOO_BIG');
return false;
}
if (file.size > 1024 * 1024) {
fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
} else {
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
}
$('#uploadFileName3').append('<li>' + file.name + '</li>');
$('#fileSize3').html('Size: ' + fileSize);
$('#fileType3').html('Type: ' + file.type);
fd.append('uploadedFile', file);
}
}
function uploadFile (fd) {
var xhr = new XMLHttpRequest();
fd.append('userId', $('textTest3').val());
/* event listners */
xhr.upload.addEventListener("progress", contMainPage.uploadProgress, false);
xhr.addEventListener("load", contMainPage.uploadComplete, false);
xhr.addEventListener("error", contMainPage.uploadFailed, false);
xhr.addEventListener("abort", contMainPage.uploadCanceled, false);
/* Be sure to change the url below to the url of your upload server side script */
xhr.open("POST", "/main/exec");
xhr.send(fd);
}
});
위와 같이 파일을 선택할때마다 파일 사이즈의 합을 체크하고 formData에 append 시켜주면 된다.
jquery fileupload 의 경우 크로스 브라우징을 지원하기 때문에 아예 swfupload를 대체해서 사용할려고 했으나
결국 파일 사이즈 체크등에 대한 기능은 html5를 사용하는 등 제한이 있기때문에 테스트만 해보았으며
</pre>
$("#form2").fileupload({
// dataType: 'text',
url: '/main/exec',
fileInput: $('#fileToUpload2'),
// formAcceptCharset: 'utf-8',
// singleFileUploads: true,
// replaceFileInput: false,
// forceIframeTransport: true,
add: function (e, data) {
if (!data.curFileSize) {
data.curFileSize = 0;
}
data.curFileSize += data.files[0].size;
if (data.curFileSize > maxFileSize) {
alert('FILE_SIZE_TOO_BIG');
return false;
}
$('#uploadFileName2').append('<li>' + data.files[0].name + '</li>');
$('#btnSubmit2').click(function() {
data.submit();
});
},
done: function (e, data) {
alert(data.result.msg);
}
,failed: function (e, data) {
alert('registFail');
}
});
<pre>
위와 같이 선언해주면 된다.
jQuery .on() 사용법
http://www.mimul.com/pebble/default/2013/03/06/1362559765579.html
위 블로그에 보면 .bind(), .live(), .delegate() 대신 .on() 의 사용을 권고하고 있습니다.^^
근데 live 대신 쓰기 위해서는 아래와 같은 방법으로 작성해야된다고 해서 실제로 테스트해보았습니다.
$(document).on("click", ".foo", handler);
<!DOCTYPE html> <html> <head> <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script> <meta charset=utf-8 /> <title>JS Bin</title> </head> <body> <input type="button" value="추가" /> <div id="parent"></div> </body> </html>
$(document).ready(function(){
var i = 0;
$('input').on('click', function(){
$('#parent').append('<ul class="child">test = '+i+'</ul>');
i++;
});
$(document).on('click', '.child', function(){
alert($(this).html());
});
/* 작동 안함.
$('.child').on(function() {
alert($(this).html());
});
*/
});
Gearman 모니터링 with PHP
1. 따로 웹서버 올리기.
https://github.com/yugene/Gearman-Monitor
따로 올릴때는 위에 소스를 clone해서 아파치 설정과
_config,php 파일에 아래와 같이 추가하면 바로 사용 가능
<?php /** * Gearman Monitor configuration file * * The following server fields are available: * - address: Gearman server address, hostname and port * - name: Gearman server name to display in Gearman Monitor interface * * Example: * $cfgServers[$i]['address'] = '192.168.0.10:4730'; * $cfgServers[$i]['name'] = 'Gearman server 1'; * ++ $i; * * $cfgServers[$i]['address'] = '192.168.1.1:7003'; * $cfgServers[$i]['name'] = 'Gearman server 2'; * ++ $i; */ $i = 0; $cfgServers = array(); $cfgServers[$i]['address'] = '192.168.0.1:4730'; $cfgServers[$i]['name'] = 'Gearman Job Server 1'; ++ $i; $cfgServers[$i]['address'] = 'gearman-job-001.test.com:4730'; $cfgServers[$i]['name'] = 'Gearman Job Server 1'; ++ $i; $cfgServers[$i]['address'] = 'gearman-job-002.test.com:4731'; $cfgServers[$i]['name'] = 'Gearman Job Server 1'; ++ $i; ?>
실행 화면
서버별 job function, queue에 쌓인 job 개수, worker 개수 등을 확인할 수 있다.
2. Net_Gearman(http://pear.php.net/package/Net_Gearman) 사용하기
<?php
@include_once('Net/Gearman/Manager.php');
class testClass
{
public function test()
{
try {
echo var_dump($this->getWorkerInfo());
echo var_dump($this->getGearmanStatus());
} catch (Exception $e) {
return $e;
}
}
public function getWorkerInfo($sServerIp='localhost', $sServerPort='4730')
{
try {
if(class_exists('Net_Gearman_Manager', true)) {
$gearman = new Net_Gearman_Manager($sServerIp.':'.$sServerPort);
$oWorkers = $gearman->workers();
$gearman->disconnect();
unset($gearman);
return $oWorkers;
}
}
catch (Exception $e) {
return $e;
}
}
public function getGearmanStatus($sServerIp='localhost', $sServerPort='4730')
{
try {
if(class_exists('Net_Gearman_Manager', true)) {
$gearman = new Net_Gearman_Manager($sServerIp.':'.$sServerPort);
$oStatus = $gearman->status();
$gearman->disconnect();
unset($gearman);
return $oStatus;
}
}
catch (Exception $e) {
return $e;
}
}
}
$gearman = new testClass();
$gearman->test();
[Node.js] MySQL Driver Tests
추가 테스트~
사내 테스트용 가상서버를 받아서 아래 내용을 추가로 테스트 해보았습니다.
가상서버의 스팩은 아래와 같습니다.
CentOS 6.x
Intel® Xeon® Processor E5506
(4M Cache, 2.13 GHz, 4.80 GT/s Intel® QPI)
결과는
| Driver/ORM | Type | Run 1 | Run 2 | Run 3 | Average|+----------------------+--------+--------+--------+--------+--------+| NodeJS_mysql | driver | 12.035 | 12.081 | 12.024 | 12.04 || NodeJS_sequelize_1 | orm | 32.908 | 30.707 | 31.118 | 31.57 || NodeJS_sequelize_10 | orm | 29.983 | 29.593 | 29.832 | 29.80 || NodeJS_mysql_queue | trans | 23.666 | 11.337 | 11.689 | 15.56 || PHP_PDO | driver | 2.889 | 2.939 | 2.984 | 2.93 || NodeJS_mysql | driver | 0.021 | 0.022 | 0.018 | .02 || NodeJS_sequelize | orm | 0.076 | 0.064 | 0.049 | .06 || PHP_PDO | driver | 0.004 | 0.003 | 0.004 | 0 |+----------------------+--------+--------+--------+--------+--------+와 같습니다. insert시 순위가 바뀌었는데요;;
확인해 본결과 CPU 때문이 않을까 싶습니다.
node 같은경우 CPU가 100% 까지도 올라갔었습니다.;;
php는 상대적으로 20%까지 올라가고 끝났습니다.
아래 테스트를 진행했던 개인 PC의 사양은
Intel® Core™ i5-760 Processor
(8M Cache, 2.80 GHz)
입니다.
http://www.cpubenchmark.net를 참고해보니
i5-760 = 4000
Xeon E5506 = 3189
이네요.
마지막 추가로
node.js에 node-db-mysql 을 테스트 해보았습니다.
node-db-mysql는 libmysql을 사용합니다.
결과는 같은 가상서버에서 PHP_PDO와 insert시 같은 성능을 내주었습니다만
select시에는 이상하게 NodeJS_mysql와 비슷하거나 약간 나은 성능을 보여주네요;
———————————————————————————–
node.js 로 새로운 프로젝트 하는중에 mysql driver가 필요해서 검색하던중 아래와 같은 글을 찾았다.
For MySQL: Goodbye NodeJS, Welcome Back PHP
저자는 node.js 팬이기는 하지만 node.js와 mysql 간에는 궁합이 안맞다고.. php가 성능이 훨씬 빠르다는 것이다.
그러면서 테스트 결과와 git repo를 올려줘서 궁금한김에 직접 테스트 하다가 이것 저것 고쳐보니 의외의 결과가 나왔다.
우선 php는 잘 할줄 모르고 귀찮아 php용 orm 은 설치하지 않음.
https://github.com/teragoon/MySQL-Driver-ORM-Tests
원 저작자(David)의 결과
| Driver/ORM | Type | Run 1 | Run 2 | Run 3 |Average|+----------------------+--------+-------+-------+-------+-------+| NodeJS_mysql | driver | 4.53 | 4.58 | 4.65 | 4.58 || NodeJS_sequelize | orm | 11.64 | 11.70 | 11.74 | 11.69 || PHP_PDO | driver | 2.14 | 2.21 | 2.21 | 2.18 || PHP_laravel_raw | driver | 3.33 | 3.37 | 3.38 | 3.36 || PHP_laravel_eloquent | orm | 4.52 | 4.38 | 4.37 | 4.42 || PHP_doctrine | orm | 2.43 | 2.44 | 2.54 | 2.47 || PHP_zenddb | driver | 2.39 | 2.48 | 2.48 | 2.45 |+----------------------+--------+-------+-------+-------+-------+내가 해본 결과
| Driver/ORM | Type | Run 1 | Run 2 | Run 3 | Average|+----------------------+--------+--------+--------+--------+--------+| NodeJS_mysql | driver | 43.148 | 34.969 | 34.792 | || NodeJS_sequelize_1 | orm | 46.830 | 52.933 | 49.480 | || NodeJS_sequelize_10 | orm | 13.408 | 13.241 | 12.648 | || NodeJS_mysql_queue | trans | 5.358 | 4.880 | 4.741 | || PHP_PDO | driver | 32.905 | 36.375 | 31.431 | |+----------------------+--------+--------+--------+--------+--------+일단 전체적으로 David의 결과가 빠른 이유는 ssd상에(ㅠㅠ) 테스트 한 결과임
그리고 실제로 node-mysql 과 php_pdo 를 비교했을때 pdo가 빠름(심하게는 10초). 또한 node용 orm 의 경우 그 차이가 더 심함
하지만 sequelize의 경우 connection pool 을 지원하기 때문에
connection pool을 1개에서 10개로 늘리면 pdo의 1/3수준.
거기에 node-mysql의 queue 및 transaction 기능 확장 모듈인 node-mysql-queue 를 이용해 insert시 transaction을 사용시 1/6로 줄어듬.
php도 connection pool이나 transction을 지원하는 드라이버나 pdo로 사용하는 방법이 있는지는 모르겠지만..^^;
혹시 가능하신분은 git fork 해서 php 구현해주시면 감사.^^
결론은 driver 성능도 중요하지만 insert의 경우 connection pool이나 transaction 같은 기능이 가장 중요하다는 엉뚱한? 결과를 도출.
David가 마지막 결론에 Pick the right tool for the right job 이라고 했는데 right way 라는 말을 추가하고 싶음.
추가
순수 select 성능 비교
| NodeJS_mysql | driver | 0.108 | 0.188 | 0.196 | || NodeJS_sequelize | orm | 0.133 | 0.132 | 0.132 | || PHP_PDO | driver | 0.004 | 0.004 | 0.003 | |PDO 압승!
P.S.
node.js의 mysql driver중에 node-db-mysql 가 있는데
http://nodejsdb.org/
node-db is developed with C++ 라는 문구가 있음.
실제 mysql client libraries 가 있어야 함.
windows가 설치 못함.
추후 비교 예정.
[Mysql] PROCEDURE를 이용한 loop insert
개발하다 보면 테스트를 위해 더미 데이터를 insert 해야 하는 경우가 자주 생긴다
그때 많은 데이터를 넣어야 하는 경우 다음과 같이 sql을 작성하면 편하다.
참고로 AES_ENCRYPT 는 암호화 function이고 CONCAT은 string 연결 function이다.
DELIMITER $$
CREATE PROCEDURE myFunction()
BEGIN
DECLARE i INT DEFAULT 1;
WHILE (i < 500000) DO
INSERT INTO `test table` (num, name, email)
VALUE (i, CONCAT(‘test’, i), HEX(AES_ENCRYPT(CONCAT(‘test’, i, ‘@test.com’), ‘key’)));
SET i = i + 1;
END WHILE;
END$$
DELIMITER ;
CALL myFunction();
위에 처럼 PROCEDURE를 만들고
while문을 이용해서 반복해주면 됨다.
socket.io 로 채팅 사이트 구현.
이미 기존에도 간단히 socket.io로 채팅 기능을 구현해 보았으나,
최근에 여유가 생겨 좀더 신경써서 만들어 보았습니다.
기본적으로 node.js 위에 express를 이용했고
서버와 클라이언트 간의 통신은 모두 socket.io를 사용하면서
사용자 / 방 정보 등을 저장하기 위해 redis를 사용했습니다.
물론, 대화 내용은 저장하지 않습니다.
view는 처음에는 jquery mobile을 사용하려고 했으나 Ajax 방식이 불편해서
Bootstrap 2.1을 사용했습니다.
기능
사용자 관련 : 가입 / 로그인 / 중복체크
채팅 관련 : 방생성 / 입장/퇴장 알림 / 메시지 전달 / 방리스트 조회
http://github.com/teragoon/node-socketio-test.git
에서 clone 받으신 후 서버를 띄우면 테스트 가능합니다.^^
[MongoDB] 간단하게 다른 DB로 collection 복사하기.
test1 DB에 있는 A collection의 내용 전체 / 일부를
test2 DB에 B collection에 복사하고 싶을때 다음과 같이 간단히 할 수 있다.
mongos> show dbs
test1
test2
mongos> use test1
switched to db test1
mongos> db2 = new Mongo(‘hostname’).getDB(‘test2′)
test2
mongos> db.badges.find().count()
59
mongos> db2.badges.find().count()
0
mongos> db.badges.find().forEach(function (c) { db2.badges.insert(c) })
mongos> use test2
switched to db test2
mongos> db.badges.find().count()
59
이상.

