perl catalyst 를 사용해 jquery file upload 를 구현.
html - angularjs.tx
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery File Upload Demo - AngularJS version</title>
<!-- Bootstrap styles -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<!-- Generic page styles -->
<link rel="stylesheet" href="/vendor/jQuery-File-Upload-9.8.0/css/style.css">
<!-- blueimp Gallery styles -->
<link rel="stylesheet" href="//blueimp.github.io/Gallery/css/blueimp-gallery.min.css">
<!-- CSS to style the file input field as button and adjust the Bootstrap progress bars -->
<link rel="stylesheet" href="/vendor/jQuery-File-Upload-9.8.0/css/jquery.fileupload.css">
<link rel="stylesheet" href="/vendor/jQuery-File-Upload-9.8.0/css/jquery.fileupload-ui.css">
<!-- CSS adjustments for browsers with JavaScript disabled -->
<noscript><link rel="stylesheet" href="/vendor/jQuery-File-Upload-9.8.0/css/jquery.fileupload-noscript.css"></noscript>
<noscript><link rel="stylesheet" href="/vendor/jQuery-File-Upload-9.8.0/css/jquery.fileupload-ui-noscript.css"></noscript>
<style>
/* Hide Angular JS elements before initializing */
.ng-cloak {
display: none;
}
</style>
</head>
<body>
<div class="container">
<!-- The file upload form used as target for the file upload widget -->
<form id="fileupload" action="/jqueryupload/upload" method="POST" enctype="multipart/form-data" data-ng-app="demo" data-ng-controller="DemoFileUploadController" data-file-upload="options" data-ng-class="{'fileupload-processing': processing() || loadingFiles}">
<!-- Redirect browsers with JavaScript disabled to the origin page -->
<noscript><input type="hidden" name="redirect" value="/jqueryupload/upload"></noscript>
<!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
<div class="row fileupload-buttonbar">
<div class="col-lg-7">
<!-- The fileinput-button span is used to style the file input field as button -->
<span class="btn btn-success fileinput-button" ng-class="{disabled: disabled}">
<i class="glyphicon glyphicon-plus"></i>
<span>Add files...</span>
<input type="file" name="files[]" multiple ng-disabled="disabled">
</span>
<button type="button" class="btn btn-primary start" data-ng-click="submit()">
<i class="glyphicon glyphicon-upload"></i>
<span>Start upload</span>
</button>
<button type="button" class="btn btn-warning cancel" data-ng-click="cancel()">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel upload</span>
</button>
<!-- The global file processing state -->
<span class="fileupload-process"></span>
</div>
<!-- The global progress state -->
<div class="col-lg-5 fade" data-ng-class="{in: active()}">
<!-- The global progress bar -->
<div class="progress progress-striped active" data-file-upload-progress="progress()"><div class="progress-bar progress-bar-success" data-ng-style="{width: num + '%'}"></div></div>
<!-- The extended global progress state -->
<div class="progress-extended"> </div>
</div>
</div>
<!-- The table listing the files available for upload/download -->
<table class="table table-striped files ng-cloak">
<tr data-ng-repeat="file in queue" data-ng-class="{'processing': file.$processing()}">
<td data-ng-switch data-on="!!file.thumbnailUrl">
<div class="preview" data-ng-switch-when="true">
<a data-ng-href="{{file.url}}" title="{{file.name}}" download="{{file.name}}" data-gallery><img data-ng-src="{{file.thumbnailUrl}}" alt=""></a>
</div>
<div class="preview" data-ng-switch-default data-file-upload-preview="file"></div>
</td>
<td>
<p class="name" data-ng-switch data-on="!!file.url">
<span data-ng-switch-when="true" data-ng-switch data-on="!!file.thumbnailUrl">
<a data-ng-switch-when="true" data-ng-href="{{file.url}}" title="{{file.name}}" download="{{file.name}}" data-gallery>{{file.name}}</a>
<a data-ng-switch-default data-ng-href="{{file.url}}" title="{{file.name}}" download="{{file.name}}">{{file.name}}</a>
</span>
<span data-ng-switch-default>{{file.name}}</span>
</p>
<strong data-ng-show="file.error" class="error text-danger">{{file.error}}</strong>
</td>
<td>
<p class="size">{{file.size | formatFileSize}}</p>
<div class="progress progress-striped active fade" data-ng-class="{pending: 'in'}[file.$state()]" data-file-upload-progress="file.$progress()"><div class="progress-bar progress-bar-success" data-ng-style="{width: num + '%'}"></div></div>
</td>
<td>
<button type="button" class="btn btn-primary start" data-ng-click="file.$submit()" data-ng-hide="!file.$submit || options.autoUpload" data-ng-disabled="file.$state() == 'pending' || file.$state() == 'rejected'">
<i class="glyphicon glyphicon-upload"></i>
<span>Start</span>
</button>
<button type="button" class="btn btn-warning cancel" data-ng-click="file.$cancel()" data-ng-hide="!file.$cancel">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel</span>
</button>
<button data-ng-controller="FileDestroyController" type="button" class="btn btn-danger destroy" data-ng-click="file.$destroy()" data-ng-hide="!file.$destroy">
<i class="glyphicon glyphicon-trash"></i>
<span>Delete</span>
</button>
</td>
</tr>
</table>
</form>
<br>
</div>
<!-- The blueimp Gallery widget -->
<div id="blueimp-gallery" class="blueimp-gallery blueimp-gallery-controls" data-filter=":even">
<div class="slides"></div>
<h3 class="title"></h3>
<a class="prev">‹</a>
<a class="next">›</a>
<a class="close">×</a>
<a class="play-pause"></a>
<ol class="indicator"></ol>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!-- The jQuery UI widget factory, can be omitted if jQuery UI is already included -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/vendor/jquery.ui.widget.js"></script>
<!-- The Load Image plugin is included for the preview images and image resizing functionality -->
<script src="//blueimp.github.io/JavaScript-Load-Image/js/load-image.all.min.js"></script>
<!-- The Canvas to Blob plugin is included for image resizing functionality -->
<script src="//blueimp.github.io/JavaScript-Canvas-to-Blob/js/canvas-to-blob.min.js"></script>
<!-- Bootstrap JS is not required, but included for the responsive demo navigation -->
<script src="//netdna.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<!-- blueimp Gallery script -->
<script src="//blueimp.github.io/Gallery/js/jquery.blueimp-gallery.min.js"></script>
<!-- The Iframe Transport is required for browsers without support for XHR file uploads -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.iframe-transport.js"></script>
<!-- The basic File Upload plugin -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload.js"></script>
<!-- The File Upload processing plugin -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload-process.js"></script>
<!-- The File Upload image preview & resize plugin -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload-image.js"></script>
<!-- The File Upload audio preview plugin -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload-audio.js"></script>
<!-- The File Upload video preview plugin -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload-video.js"></script>
<!-- The File Upload validation plugin -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload-validate.js"></script>
<!-- The File Upload Angular JS module -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload-angular.js"></script>
<!-- The main application script -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/app.js"></script>
</body>
</html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery File Upload Demo - AngularJS version</title>
<!-- Bootstrap styles -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<!-- Generic page styles -->
<link rel="stylesheet" href="/vendor/jQuery-File-Upload-9.8.0/css/style.css">
<!-- blueimp Gallery styles -->
<link rel="stylesheet" href="//blueimp.github.io/Gallery/css/blueimp-gallery.min.css">
<!-- CSS to style the file input field as button and adjust the Bootstrap progress bars -->
<link rel="stylesheet" href="/vendor/jQuery-File-Upload-9.8.0/css/jquery.fileupload.css">
<link rel="stylesheet" href="/vendor/jQuery-File-Upload-9.8.0/css/jquery.fileupload-ui.css">
<!-- CSS adjustments for browsers with JavaScript disabled -->
<noscript><link rel="stylesheet" href="/vendor/jQuery-File-Upload-9.8.0/css/jquery.fileupload-noscript.css"></noscript>
<noscript><link rel="stylesheet" href="/vendor/jQuery-File-Upload-9.8.0/css/jquery.fileupload-ui-noscript.css"></noscript>
<style>
/* Hide Angular JS elements before initializing */
.ng-cloak {
display: none;
}
</style>
</head>
<body>
<div class="container">
<!-- The file upload form used as target for the file upload widget -->
<form id="fileupload" action="/jqueryupload/upload" method="POST" enctype="multipart/form-data" data-ng-app="demo" data-ng-controller="DemoFileUploadController" data-file-upload="options" data-ng-class="{'fileupload-processing': processing() || loadingFiles}">
<!-- Redirect browsers with JavaScript disabled to the origin page -->
<noscript><input type="hidden" name="redirect" value="/jqueryupload/upload"></noscript>
<!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
<div class="row fileupload-buttonbar">
<div class="col-lg-7">
<!-- The fileinput-button span is used to style the file input field as button -->
<span class="btn btn-success fileinput-button" ng-class="{disabled: disabled}">
<i class="glyphicon glyphicon-plus"></i>
<span>Add files...</span>
<input type="file" name="files[]" multiple ng-disabled="disabled">
</span>
<button type="button" class="btn btn-primary start" data-ng-click="submit()">
<i class="glyphicon glyphicon-upload"></i>
<span>Start upload</span>
</button>
<button type="button" class="btn btn-warning cancel" data-ng-click="cancel()">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel upload</span>
</button>
<!-- The global file processing state -->
<span class="fileupload-process"></span>
</div>
<!-- The global progress state -->
<div class="col-lg-5 fade" data-ng-class="{in: active()}">
<!-- The global progress bar -->
<div class="progress progress-striped active" data-file-upload-progress="progress()"><div class="progress-bar progress-bar-success" data-ng-style="{width: num + '%'}"></div></div>
<!-- The extended global progress state -->
<div class="progress-extended"> </div>
</div>
</div>
<!-- The table listing the files available for upload/download -->
<table class="table table-striped files ng-cloak">
<tr data-ng-repeat="file in queue" data-ng-class="{'processing': file.$processing()}">
<td data-ng-switch data-on="!!file.thumbnailUrl">
<div class="preview" data-ng-switch-when="true">
<a data-ng-href="{{file.url}}" title="{{file.name}}" download="{{file.name}}" data-gallery><img data-ng-src="{{file.thumbnailUrl}}" alt=""></a>
</div>
<div class="preview" data-ng-switch-default data-file-upload-preview="file"></div>
</td>
<td>
<p class="name" data-ng-switch data-on="!!file.url">
<span data-ng-switch-when="true" data-ng-switch data-on="!!file.thumbnailUrl">
<a data-ng-switch-when="true" data-ng-href="{{file.url}}" title="{{file.name}}" download="{{file.name}}" data-gallery>{{file.name}}</a>
<a data-ng-switch-default data-ng-href="{{file.url}}" title="{{file.name}}" download="{{file.name}}">{{file.name}}</a>
</span>
<span data-ng-switch-default>{{file.name}}</span>
</p>
<strong data-ng-show="file.error" class="error text-danger">{{file.error}}</strong>
</td>
<td>
<p class="size">{{file.size | formatFileSize}}</p>
<div class="progress progress-striped active fade" data-ng-class="{pending: 'in'}[file.$state()]" data-file-upload-progress="file.$progress()"><div class="progress-bar progress-bar-success" data-ng-style="{width: num + '%'}"></div></div>
</td>
<td>
<button type="button" class="btn btn-primary start" data-ng-click="file.$submit()" data-ng-hide="!file.$submit || options.autoUpload" data-ng-disabled="file.$state() == 'pending' || file.$state() == 'rejected'">
<i class="glyphicon glyphicon-upload"></i>
<span>Start</span>
</button>
<button type="button" class="btn btn-warning cancel" data-ng-click="file.$cancel()" data-ng-hide="!file.$cancel">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel</span>
</button>
<button data-ng-controller="FileDestroyController" type="button" class="btn btn-danger destroy" data-ng-click="file.$destroy()" data-ng-hide="!file.$destroy">
<i class="glyphicon glyphicon-trash"></i>
<span>Delete</span>
</button>
</td>
</tr>
</table>
</form>
<br>
</div>
<!-- The blueimp Gallery widget -->
<div id="blueimp-gallery" class="blueimp-gallery blueimp-gallery-controls" data-filter=":even">
<div class="slides"></div>
<h3 class="title"></h3>
<a class="prev">‹</a>
<a class="next">›</a>
<a class="close">×</a>
<a class="play-pause"></a>
<ol class="indicator"></ol>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!-- The jQuery UI widget factory, can be omitted if jQuery UI is already included -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/vendor/jquery.ui.widget.js"></script>
<!-- The Load Image plugin is included for the preview images and image resizing functionality -->
<script src="//blueimp.github.io/JavaScript-Load-Image/js/load-image.all.min.js"></script>
<!-- The Canvas to Blob plugin is included for image resizing functionality -->
<script src="//blueimp.github.io/JavaScript-Canvas-to-Blob/js/canvas-to-blob.min.js"></script>
<!-- Bootstrap JS is not required, but included for the responsive demo navigation -->
<script src="//netdna.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<!-- blueimp Gallery script -->
<script src="//blueimp.github.io/Gallery/js/jquery.blueimp-gallery.min.js"></script>
<!-- The Iframe Transport is required for browsers without support for XHR file uploads -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.iframe-transport.js"></script>
<!-- The basic File Upload plugin -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload.js"></script>
<!-- The File Upload processing plugin -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload-process.js"></script>
<!-- The File Upload image preview & resize plugin -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload-image.js"></script>
<!-- The File Upload audio preview plugin -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload-audio.js"></script>
<!-- The File Upload video preview plugin -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload-video.js"></script>
<!-- The File Upload validation plugin -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload-validate.js"></script>
<!-- The File Upload Angular JS module -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/jquery.fileupload-angular.js"></script>
<!-- The main application script -->
<script src="/vendor/jQuery-File-Upload-9.8.0/js/app.js"></script>
</body>
</html>
controller - Jqueryupload.pm
package Lars::Web::Controller::Jqueryupload;
use Moose;
use namespace::autoclean;
use utf8;
use JSON::XS;
BEGIN { extends 'Catalyst::Controller' }
sub index :Path :Args(0) {
my ($self, $c) = @_;
$c->stash->{template} = 'jqueryupload.tx';
}
use Moose;
use namespace::autoclean;
use utf8;
use JSON::XS;
BEGIN { extends 'Catalyst::Controller' }
sub index :Path :Args(0) {
my ($self, $c) = @_;
$c->stash->{template} = 'jqueryupload.tx';
}
sub jqueryui :Path("angularjs") :Args(0) {
my ($self, $c) = @_;
$c->stash->{template} = 'angularjs.tx';
}
my ($self, $c) = @_;
$c->stash->{template} = 'angularjs.tx';
}
# 업로드 구현
sub upload :Path("upload") :Args(0) :POST{
my ($self, $c) = @_;
my $file_upload = $c->req->upload('files[]');
my $files;
if ($file_upload) {
$files = Lars::Util::Forum::image_path($file_upload->filename);
$c->model('FsStore')->put_from_file($files, $file_upload->fh);
}
my $file = $c->model('DB::Attachment')->create(
{
create_date => \'now()',
name => $file_upload->filename,
path => $files,
},
);
my $new_list = [
{
url=>$files,
thumbnail_url=>$files,
name=>$file_upload->filename,
size=>$file_upload->size,
delete_url=>'http://url.to/delete /file/',
delete_type=> 'DELETE'
}
];
$c->stash->{files} = $new_list;
$c->forward('JSON');
return;
}
my ($self, $c) = @_;
my $file_upload = $c->req->upload('files[]');
my $files;
if ($file_upload) {
$files = Lars::Util::Forum::image_path($file_upload->filename);
$c->model('FsStore')->put_from_file($files, $file_upload->fh);
}
my $file = $c->model('DB::Attachment')->create(
{
create_date => \'now()',
name => $file_upload->filename,
path => $files,
},
);
my $new_list = [
{
url=>$files,
thumbnail_url=>$files,
name=>$file_upload->filename,
size=>$file_upload->size,
delete_url=>'http://url.to/delete /file/',
delete_type=> 'DELETE'
}
];
$c->stash->{files} = $new_list;
$c->forward('JSON');
return;
}
util - Forum.pm
package Lars::Util::Forum;
use strict;
use warnings;
use Data::Random qw/rand_chars/;
use Exporter 'import';
our @EXPORT_OK = qw/image_path/;
sub image_path {
my $filename = shift;
my $dt = DateTime->now;
if ($filename =~ /\.(jpe?g|gif|png)$/i) {
my $ext = lc $1;
my $root = join '', '/image/', $dt->year, '/', $dt->month, '/', $dt->day;
# my $name = Encode::encode('utf8', $filename);
my $name = join '', time, rand_chars(size => 8, set => 'alphanumeric');
# return $root.'/'.$name;
return $root.'/'.$name . '.' . $ext;
}
return undef;
}
use strict;
use warnings;
use Data::Random qw/rand_chars/;
use Exporter 'import';
our @EXPORT_OK = qw/image_path/;
sub image_path {
my $filename = shift;
my $dt = DateTime->now;
if ($filename =~ /\.(jpe?g|gif|png)$/i) {
my $ext = lc $1;
my $root = join '', '/image/', $dt->year, '/', $dt->month, '/', $dt->day;
# my $name = Encode::encode('utf8', $filename);
my $name = join '', time, rand_chars(size => 8, set => 'alphanumeric');
# return $root.'/'.$name;
return $root.'/'.$name . '.' . $ext;
}
return undef;
}
JSON Response 때문에 고생을 좀 했음. -_-... 굉장히 쓸데없는 고생었음.
{ files: [ { url: "http://url.to/file/or/page", thumbnail_url: "http://url.to/thumnail.jpg ", name: "thumb2.jpg", type: "image/jpeg", size: 46353, delete_url: "http://url.to/delete /file/", delete_type: "DELETE" } ] }
이런 모양으로 받으면 됨.
빨간줄친모양으로 리턴 받으면 잘 됨.
댓글
댓글 쓰기