1package jQuery::File::Upload; 2 3use 5.008008; 4use strict; 5#use warnings; 6 7use CGI; 8use JSON::XS; 9use JSON; 10use Net::SSH2; 11use Net::SSH2::SFTP; 12use Image::Magick; 13use Cwd 'abs_path'; 14use URI; 15use Data::GUID; 16 17#use LWP::UserAgent; 18#use LWP::Protocol::https; 19 20our $VERSION = '0.30'; 21 22my %errors = ( 23 '_validate_max_file_size' => 'File is too big', 24 '_validate_min_file_size' => 'File is too small', 25 '_validate_accept_file_types' => 'Filetype not allowed', 26 '_validate_reject_file_types' => 'Filetype not allowed', 27 '_validate_max_number_of_files' => 'Maximum number of files exceeded', 28 '_validate_max_width' => 'Image exceeds maximum width', 29 '_validate_min_width' => 'Image requires a minimum width', 30 '_validate_max_height' => 'Image exceeds maximum height', 31 '_validate_min_height' => 'Image requires a minimum height' 32); 33 34#GETTERS/SETTERS 35sub new { 36 my $invocant = shift; 37 my $class = ref($invocant) || $invocant; 38 my $self = { 39 field_name => 'files[]', 40 ctx => undef, 41 cgi => undef, 42 thumbnail_width => 80, 43 thumbnail_height => 80, 44 thumbnail_quality => 70, 45 thumbnail_format => 'jpg', 46 thumbnail_density => undef, 47 format => 'jpg', 48 quality => 70, 49 50 process_images => 1, 51 thumbnail_filename => undef, 52 thumbnail_prefix => 'thumb_', 53 thumbnail_postfix => '', 54 filename => undef, 55 client_filename => undef, 56 show_client_filename => 1, 57 use_client_filename => undef, 58 filename_salt => '', 59 copy_file => 0, 60 script_url => undef, 61 tmp_dir => '/tmp', 62 should_delete => 1, 63 64 absolute_filename => undef, 65 absolute_thumbnail_filename => undef, 66 67 delete_params => [], 68 69 upload_dir => undef, 70 thumbnail_upload_dir => undef, 71 upload_url_base => undef, 72 thumbnail_url_base => undef, 73 relative_url_path => '/files', 74 thumbnail_relative_url_path => undef, 75 relative_to_host => undef, 76 delete_url => undef, 77 78 data => {}, 79 80 #callbacks 81 post_delete => sub {}, 82 post_post => sub {}, 83 post_get => sub {}, 84 85 #pre calls 86 pre_delete => sub {}, 87 pre_post => sub {}, 88 pre_get => sub {}, 89 90 #scp/rcp login info 91 scp => [], 92 93 #user validation specifications 94 max_file_size => undef, 95 min_file_size => 1, 96 accept_file_types => [], 97 reject_file_types => [], 98 require_image => undef, 99 max_width => undef, 100 max_height => undef, 101 min_width => 1, 102 min_height => 1, 103 max_number_of_files => undef, 104 105 #not to be used by users 106 output => undef, 107 handle => undef, 108 tmp_filename => undef, 109 fh => undef, 110 error => undef, 111 upload => undef, 112 file_type => undef, 113 is_image => undef, 114 image_magick => undef, 115 width => undef, 116 height => undef, 117 num_files_in_dir => undef, 118 user_error => undef, 119 @_, # Override previous attributes 120 }; 121 return bless $self, $class; 122} 123 124sub upload_dir { 125 my $self = shift; 126 127 if (@_) { 128 $self->{upload_dir} = shift; 129 } 130 131 #set upload_dir to directory of this script if not provided 132 if(!(defined $self->{upload_dir})) { 133 $self->{upload_dir} = abs_path($0); 134 $self->{upload_dir} =~ s/(.*)\/.*/$1/; 135 $self->{upload_dir} .= '/files'; 136 } 137 138 return $self->{upload_dir}; 139} 140 141sub thumbnail_upload_dir { 142 my $self = shift; 143 144 if (@_) { 145 $self->{thumbnail_upload_dir} = shift; 146 } 147 148 #set upload_dir to directory of this script if not provided 149 if(!(defined $self->{thumbnail_upload_dir})) { 150 $self->{thumbnail_upload_dir} = $self->upload_dir; 151 } 152 153 return $self->{thumbnail_upload_dir}; 154} 155 156sub upload_url_base { 157 my $self = shift; 158 159 if (@_) { 160 $self->{upload_url_base} = shift; 161 } 162 163 if(!(defined $self->{upload_url_base})) { 164 $self->{upload_url_base} = $self->_url_base . $self->relative_url_path; 165 } 166 167 return $self->{upload_url_base}; 168} 169 170sub _url_base { 171 my $self = shift; 172 my $url; 173 174 if($self->relative_to_host) { 175 $url = $self->{uri}->scheme . '://' . $self->{uri}->host; 176 } 177 else { 178 $url = $self->script_url; 179 $url =~ s/(.*)\/.*/$1/; 180 } 181 182 return $url; 183} 184 185sub thumbnail_url_base { 186 my $self = shift; 187 188 if (@_) { 189 $self->{thumbnail_url_base} = shift; 190 } 191 192 if(!(defined $self->{thumbnail_url_base})) { 193 if(defined $self->thumbnail_relative_url_path) { 194 $self->{thumbnail_url_base} = $self->_url_base . $self->thumbnail_relative_url_path; 195 } 196 else { 197 $self->{thumbnail_url_base} = $self->upload_url_base; 198 } 199 } 200 201 return $self->{thumbnail_url_base}; 202} 203 204 205sub relative_url_path { 206 my $self = shift; 207 208 if(@_) { 209 $self->{relative_url_path} = shift; 210 } 211 212 return $self->{relative_url_path}; 213} 214 215sub thumbnail_relative_url_path { 216 my $self = shift; 217 218 if(@_) { 219 $self->{thumbnail_relative_url_path} = shift; 220 } 221 222 return $self->{thumbnail_relative_url_path}; 223} 224 225sub relative_to_host { 226 my $self = shift; 227 228 if(@_) { 229 $self->{relative_to_host} = shift; 230 } 231 232 return $self->{relative_to_host}; 233} 234 235 236 237sub field_name { 238 my $self = shift; 239 240 if (@_) { 241 $self->{field_name} = shift; 242 } 243 244 return $self->{field_name}; 245} 246 247sub ctx { 248 my $self = shift; 249 250 if (@_) { 251 $self->{ctx} = shift; 252 } 253 254 return $self->{ctx}; 255} 256 257sub cgi { 258 my $self = shift; 259 260 if (@_) { 261 $self->{cgi} = shift; 262 } 263 $self->{cgi} = CGI->new unless defined $self->{cgi}; 264 265 return $self->{cgi}; 266} 267 268sub should_delete { 269 my $self = shift; 270 271 if (@_) { 272 $self->{should_delete} = shift; 273 } 274 275 return $self->{should_delete}; 276} 277 278sub scp { 279 my $self = shift; 280 281 if (@_) { 282 $self->{scp} = shift; 283 } 284 285 return $self->{scp}; 286} 287 288sub max_file_size { 289 my $self = shift; 290 291 if (@_) { 292 $self->{max_file_size} = shift; 293 } 294 295 return $self->{max_file_size}; 296} 297 298sub min_file_size { 299 my $self = shift; 300 301 if (@_) { 302 $self->{min_file_size} = shift; 303 } 304 305 return $self->{min_file_size}; 306} 307 308sub accept_file_types { 309 my $self = shift; 310 311 if (@_) { 312 my $a_ref = shift; 313 die "accept_file_types must be an array ref" unless UNIVERSAL::isa($a_ref,'ARRAY'); 314 $self->{accept_file_types} = $a_ref; 315 } 316 317 if(scalar(@{$self->{accept_file_types}}) == 0 and $self->require_image) { 318 $self->{accept_file_types} = ['image/jpeg','image/jpg','image/png','image/gif']; 319 } 320 321 return $self->{accept_file_types}; 322} 323 324sub reject_file_types { 325 my $self = shift; 326 327 if (@_) { 328 my $a_ref = shift; 329 die "reject_file_types must be an array ref" unless UNIVERSAL::isa($a_ref,'ARRAY'); 330 $self->{reject_file_types} = $a_ref; 331 } 332 333 return $self->{reject_file_types}; 334} 335 336sub require_image { 337 my $self = shift; 338 339 if (@_) { 340 $self->{require_image} = shift; 341 } 342 343 return $self->{require_image}; 344} 345 346sub delete_params { 347 my $self = shift; 348 349 if (@_) { 350 my $a_ref = shift; 351 die "delete_params must be an array ref" unless UNIVERSAL::isa($a_ref,'ARRAY'); 352 $self->{delete_params} = $a_ref; 353 } 354 355 return $self->{delete_params}; 356} 357 358sub delete_url { 359 my $self = shift; 360 361 if(@_) { 362 $self->{delete_url} = shift; 363 } 364 365 return $self->{delete_url}; 366} 367 368sub thumbnail_width { 369 my $self = shift; 370 371 if (@_) { 372 $self->{thumbnail_width} = shift; 373 } 374 375 return $self->{thumbnail_width}; 376} 377 378sub thumbnail_height { 379 my $self = shift; 380 381 if (@_) { 382 $self->{thumbnail_height} = shift; 383 } 384 385 return $self->{thumbnail_height}; 386} 387 388sub thumbnail_quality { 389 my $self = shift; 390 391 if (@_) { 392 $self->{thumbnail_quality} = shift; 393 } 394 395 return $self->{thumbnail_quality}; 396} 397 398sub thumbnail_format { 399 my $self = shift; 400 401 if (@_) { 402 $self->{thumbnail_format} = shift; 403 } 404 405 return $self->{thumbnail_format}; 406} 407 408sub thumbnail_density { 409 my $self = shift; 410 411 if (@_) { 412 $self->{thumbnail_density} = shift; 413 } 414 415 return $self->{thumbnail_density}; 416} 417 418sub thumbnail_prefix { 419 my $self = shift; 420 421 if (@_) { 422 $self->{thumbnail_prefix} = shift; 423 } 424 425 return $self->{thumbnail_prefix}; 426} 427 428sub thumbnail_postfix { 429 my $self = shift; 430 431 if (@_) { 432 $self->{thumbnail_postfix} = shift; 433 } 434 435 return $self->{thumbnail_postfix}; 436} 437 438sub thumbnail_final_width { 439 my $self = shift; 440 441 if(@_) { 442 $self->{thumbnail_final_width} = shift; 443 } 444 445 return $self->{thumbnail_final_width}; 446} 447 448sub thumbnail_final_height { 449 my $self = shift; 450 451 if(@_) { 452 $self->{thumbnail_final_height} = shift; 453 } 454 455 return $self->{thumbnail_final_height}; 456} 457 458sub quality { 459 my $self = shift; 460 461 if (@_) { 462 $self->{quality} = shift; 463 } 464 465 return $self->{quality}; 466} 467 468sub format { 469 my $self = shift; 470 471 if (@_) { 472 $self->{format} = shift; 473 } 474 475 return $self->{format}; 476} 477 478sub final_width { 479 my $self = shift; 480 481 if(@_) { 482 $self->{final_width} = shift; 483 } 484 485 return $self->{final_width}; 486} 487 488sub final_height { 489 my $self = shift; 490 491 if(@_) { 492 $self->{final_height} = shift; 493 } 494 495 return $self->{final_height}; 496} 497 498sub max_width { 499 my $self = shift; 500 501 if (@_) { 502 $self->{max_width} = shift; 503 } 504 505 return $self->{max_width}; 506} 507 508sub max_height { 509 my $self = shift; 510 511 if (@_) { 512 $self->{max_height} = shift; 513 } 514 515 return $self->{max_height}; 516} 517 518sub min_width { 519 my $self = shift; 520 521 if (@_) { 522 $self->{min_width} = shift; 523 } 524 525 return $self->{min_width}; 526} 527 528sub min_height { 529 my $self = shift; 530 531 if (@_) { 532 $self->{min_height} = shift; 533 } 534 535 return $self->{min_height}; 536} 537 538sub max_number_of_files { 539 my $self = shift; 540 541 if (@_) { 542 $self->{max_number_of_files} = shift; 543 } 544 545 return $self->{max_number_of_files}; 546} 547 548sub filename { 549 my $self = shift; 550 551 if (@_) { 552 $self->{filename} = shift; 553 } 554 555 return $self->{filename}; 556} 557 558sub absolute_filename { 559 my $self = shift; 560 561 if (@_) { 562 $self->{absolute_filename} = shift; 563 } 564 565 return $self->{absolute_filename}; 566} 567 568sub thumbnail_filename { 569 my $self = shift; 570 571 if (@_) { 572 $self->{thumbnail_filename} = shift; 573 } 574 575 return $self->{thumbnail_filename}; 576} 577 578sub absolute_thumbnail_filename { 579 my $self = shift; 580 581 if (@_) { 582 $self->{absolute_thumbnail_filename} = shift; 583 } 584 585 return $self->{absolute_thumbnail_filename}; 586} 587 588sub client_filename { 589 my $self = shift; 590 591 if (@_) { 592 $self->{client_filename} = shift; 593 } 594 595 return $self->{client_filename}; 596} 597 598sub show_client_filename { 599 my $self = shift; 600 601 if (@_) { 602 $self->{show_client_filename} = shift; 603 } 604 605 return $self->{show_client_filename}; 606} 607 608sub use_client_filename { 609 my $self = shift; 610 611 if (@_) { 612 $self->{use_client_filename} = shift; 613 } 614 615 return $self->{use_client_filename}; 616} 617 618sub filename_salt { 619 my $self = shift; 620 621 if (@_) { 622 $self->{filename_salt} = shift; 623 } 624 625 return $self->{filename_salt}; 626} 627 628sub tmp_dir { 629 my $self = shift; 630 631 if (@_) { 632 $self->{tmp_dir} = shift; 633 } 634 635 return $self->{tmp_dir}; 636} 637 638sub script_url { 639 my $self = shift; 640 641 if (@_) { 642 $self->{script_url} = shift; 643 } 644 645 if(!(defined $self->{script_url})) { 646 if(defined $self->ctx) { 647 $self->{script_url} = $self->ctx->request->uri; 648 } 649 else { 650 $self->{script_url} = $ENV{SCRIPT_URI}; 651 } 652 } 653 654 return $self->{script_url}; 655} 656 657sub data { 658 my $self = shift; 659 660 if(@_) { 661 $self->{data} = shift; 662 } 663 664 return $self->{data}; 665} 666 667sub process_images { 668 my $self = shift; 669 670 if (@_) { 671 $self->{process_images} = shift; 672 } 673 674 return $self->{process_images}; 675} 676 677sub copy_file { 678 my $self = shift; 679 680 if (@_) { 681 $self->{copy_file} = shift; 682 } 683 684 return $self->{copy_file}; 685} 686 687#GETTERS 688sub output { shift->{output} } 689sub url { shift->{url} } 690sub thumbnail_url { shift->{thumbnail_url} } 691sub is_image { shift->{is_image} } 692sub size { shift->{file_size} } 693 694#OTHER METHODS 695sub print_response { 696 my $self = shift; 697 698 my $content_type = 'text/plain'; 699 if(defined $self->ctx) { 700 701 #thanks to Lukas Rampa for this suggestion 702 if ($self->ctx->req->headers->header('Accept') =~ qr(application/json) ) { 703 $content_type = 'application/json'; 704 } 705 706 $self->ctx->stash->{current_view} = ''; 707 $self->ctx->res->content_type("$content_type; charset=utf-8"); 708 $self->ctx->res->body($self->output . ""); #concatenate "" for when there is no output 709 } 710 else { 711 print "Content-type: $content_type\n\n"; 712 print $self->output; 713 } 714} 715 716sub handle_request { 717 my $self = shift; 718 my ($print) = @_; 719 720 my $method = $self->_get_request_method; 721 722 if($method eq 'GET') { 723 &{$self->pre_get}($self); 724 &{$self->post_get}($self); 725 } 726 elsif($method eq 'PATCH' or $method eq 'POST' or $method eq 'PUT') { 727 $self->{user_error} = &{$self->pre_post}($self); 728 unless($self->{user_error}) { 729 $self->_post; 730 &{$self->post_post}($self); 731 } 732 else { $self->_generate_output } 733 } 734 elsif($method eq 'DELETE') { 735 $self->{user_error} = &{$self->pre_delete}($self); #even though we may not delete, we should give user option to still run code 736 if(not $self->{user_error} and $self->should_delete) { 737 $self->_delete; 738 &{$self->post_delete}($self); 739 } 740 else { $self->_generate_output } 741 } 742 else { 743 $self->_set_status(405); 744 } 745 746 $self->print_response if $print; 747 $self->_clear; 748} 749 750sub generate_output { 751 my $self = shift; 752 my ($arr_ref) = @_; 753 754 #necessary if we are going to use _url_base via thumbnail_url_base and upload_url_base 755 $self->_set_uri; 756 757 my @arr; 758 for(@$arr_ref) { 759 my %h; 760 die "Must provide a filename in generate_output" unless exists $_->{filename}; 761 die "Must provide a size in generate_output" unless exists $_->{size}; 762 $self->{is_image} = $self->process_images && $_->{image} eq 'y' ? 1 : 0; 763 $h{size} = $_->{size}; 764 $h{error} = $_->{error}; 765 766 if(exists $_->{'name'}) { 767 $h{name} = $_->{name} 768 } 769 else { 770 $h{name} = $_->{filename}; 771 } 772 773 if($_->{filename}) { 774 $self->filename($_->{filename}); 775 } 776 777 if(exists $_->{thumbnail_filename}) { 778 $self->thumbnail_filename($_->{thumbnail_filename}); 779 } 780 else { 781 my $no_ext = $self->_no_ext; 782 $self->thumbnail_filename($self->thumbnail_prefix . $no_ext . $self->thumbnail_postfix . '.' . $self->thumbnail_format); 783 } 784 785 $self->_set_urls; 786 $h{url} = $_->{url} eq '' ? $self->url : $_->{url}; 787 $h{thumbnailUrl} = $_->{thumbnailUrl} eq '' ? $self->thumbnail_url : $_->{thumbnailUrl}; 788 789 $h{deleteUrl} = $_->{'deleteUrl'} eq '' ? $self->_delete_url($_->{delete_params}) : $_->{'deleteUrl'}; 790 $h{deleteType} = 'DELETE'; 791 push @arr, \%h; 792 793 #reset for the next time around 794 $self->delete_url(''); 795 } 796 797 #they should provide image=y or image=n if image 798 my $json = JSON::XS->new->ascii->pretty->allow_nonref; 799 $self->{output} = $json->encode({files => \@arr}); 800} 801 802sub _no_ext { 803 my $self = shift; 804 $self->filename($_->{filename}); 805 my ($no_ext) = $self->filename =~ qr/(.*)\.(.*)/; 806 return $no_ext; 807} 808 809#PRE/POST METHODS 810sub pre_delete { 811 my $self = shift; 812 813 if (@_) { 814 $self->{pre_delete} = shift; 815 } 816 817 return $self->{pre_delete}; 818} 819 820sub post_delete { 821 my $self = shift; 822 823 if (@_) { 824 $self->{post_delete} = shift; 825 } 826 827 return $self->{post_delete}; 828} 829 830sub pre_post { 831 my $self = shift; 832 833 if (@_) { 834 $self->{pre_post} = shift; 835 } 836 837 return $self->{pre_post}; 838} 839 840sub post_post { 841 my $self = shift; 842 843 if (@_) { 844 $self->{post_post} = shift; 845 } 846 847 return $self->{post_post}; 848} 849 850sub pre_get { 851 my $self = shift; 852 853 if (@_) { 854 $self->{pre_get} = shift; 855 } 856 857 return $self->{pre_get}; 858} 859 860sub post_get { 861 my $self = shift; 862 863 if (@_) { 864 $self->{post_get} = shift; 865 } 866 867 return $self->{post_get}; 868} 869 870sub _clear { 871 my $self = shift; 872 873 #clear cgi object so we get a new one for new request 874 $self->{cgi} = undef; 875 $self->{handle} = undef; 876 $self->{tmp_filename} = undef; 877 $self->{upload} = undef; 878 $self->{fh} = undef; 879 $self->{file_size} = undef; 880 $self->{error} = undef; 881 $self->{file_type} = undef; 882 $self->{is_image} = 0; 883 $self->{width} = undef; 884 $self->{height} = undef; 885 $self->{num_files_in_dir} = undef; 886 $self->{output} = undef; 887 $self->{client_filename} = undef; 888 $self->{tmp_thumb_path} = undef; 889 $self->{tmp_file_path} = undef; 890 $self->{user_error} = undef; 891} 892 893sub _post { 894 my $self = shift; 895 896 if($self->_prepare_file_attrs and $self->_validate_file) { 897 if($self->is_image) { 898 $self->_create_thumbnail; 899 $self->_create_tmp_image 900 } 901 $self->_save; 902 } 903 904 #delete temporary files 905 if($self->is_image) { 906 unlink ($self->{tmp_thumb_path}, $self->{tmp_file_path}); 907 } 908 909 #generate json output 910 $self->_generate_output; 911} 912 913sub _generate_output { 914 my $self = shift; 915 916 my $method = $self->_get_request_method; 917 my $obj; 918 919 if($method eq 'POST') { 920 my %hash; 921 unless($self->{user_error}) { 922 $hash{'url'} = $self->url; 923 $hash{'thumbnailUrl'} = $self->thumbnail_url; 924 $hash{'deleteUrl'} = $self->_delete_url; 925 $hash{'deleteType'} = 'DELETE'; 926 $hash{error} = $self->_generate_error; 927 } 928 else { 929 $self->_prepare_file_basics; 930 $hash{error} = $self->{user_error}; 931 } 932 933 $hash{'name'} = $self->show_client_filename ? $self->client_filename . "" : $self->filename; 934 $hash{'size'} = $self->{file_size}; 935 $obj->{files} = [\%hash]; 936 } 937 elsif($method eq 'DELETE') { 938 unless($self->{user_error}) { 939 $obj->{$self->_get_param('filename')} = JSON::true; 940 } 941 else { 942 $obj->{error} = $self->{user_error}; 943 } 944 } 945 946 my $json = JSON::XS->new->ascii->pretty->allow_nonref; 947 $self->{output} = $json->encode($obj); 948} 949 950sub _delete { 951 my $self = shift; 952 953 my $filename = $self->_get_param('filename'); 954 my $thumbnail_filename = $self->_get_param('thumbnail_filename'); 955 my $image_yn = $self->_get_param('image'); 956 957 if(@{$self->scp}) { 958 for(@{$self->scp}) { 959 960 my $ssh2 = $self->_auth_user($_); 961 $_->{thumbnail_upload_dir} = $_->{upload_dir} if $_->{thumbnail_upload_dir} eq ''; 962 963 my $sftp = $ssh2->sftp; 964 $sftp->unlink($_->{upload_dir} . '/' . $filename); 965 $sftp->unlink($_->{thumbnail_upload_dir} . '/' . $thumbnail_filename) if $image_yn eq 'y'; 966 } 967 } 968 else { 969 my $no_ext = $self->_no_ext; 970 unlink $self->upload_dir . '/' . $filename; 971 unlink($self->thumbnail_upload_dir . '/' . $thumbnail_filename) if $image_yn eq 'y'; 972 } 973 974 $self->_generate_output; 975} 976 977sub _get_param { 978 my $self = shift; 979 my ($param) = @_; 980 981 if(defined $self->ctx) { 982 return $self->ctx->req->params->{$param}; 983 } 984 else { 985 return $self->cgi->param($param); 986 } 987} 988 989sub _delete_url { 990 my $self = shift; 991 return if $self->delete_url ne ''; 992 my ($delete_params) = @_; 993 994 my $url = $self->script_url; 995 my $uri = $self->{uri}->clone; 996 997 my $image_yn = $self->is_image ? 'y' : 'n'; 998 999 unless(defined $delete_params and scalar(@$delete_params)) { 1000 $delete_params = []; 1001 } 1002 1003 push @$delete_params, @{$self->delete_params} if @{$self->delete_params}; 1004 push @$delete_params, ('filename',$self->filename,'image',$image_yn); 1005 push @$delete_params, ('thumbnail_filename',$self->thumbnail_filename) if $self->is_image; 1006 1007 $uri->query_form($delete_params); 1008 1009 $self->delete_url($uri->as_string); 1010 1011 return $self->delete_url; 1012} 1013 1014sub _script_url { 1015 my $self = shift; 1016 1017 if(defined $self->ctx) { 1018 return $self->ctx->request->uri; 1019 } 1020 else { 1021 return $ENV{'SCRIPT_URI'}; 1022 } 1023} 1024 1025sub _prepare_file_attrs { 1026 my $self = shift; 1027 1028 #ORDER MATTERS 1029 return unless $self->_prepare_file_basics; 1030 $self->_set_tmp_filename; 1031 $self->_set_file_type; 1032 $self->_set_is_image; 1033 $self->_set_filename; 1034 $self->_set_absolute_filenames; 1035 $self->_set_image_magick; 1036 $self->_set_width; 1037 $self->_set_height; 1038 $self->_set_num_files_in_dir; 1039 $self->_set_uri; 1040 $self->_set_urls; 1041 1042 return 1; 1043} 1044 1045sub _prepare_file_basics { 1046 my ($self) = @_; 1047 1048 return undef unless $self->_set_upload_obj; 1049 $self->_set_fh; 1050 $self->_set_file_size; 1051 $self->_set_client_filename; 1052 1053 return 1; 1054} 1055 1056sub _set_urls { 1057 my $self = shift; 1058 1059 if($self->is_image) { 1060 $self->{thumbnail_url} = $self->thumbnail_url_base . '/' . $self->thumbnail_filename; 1061 } 1062 $self->{url} = $self->upload_url_base . '/' . $self->filename; 1063} 1064 1065sub _set_uri { 1066 my $self = shift; 1067 #if catalyst, use URI already made? 1068 if(defined $self->ctx) { 1069 $self->{uri} = $self->ctx->req->uri; 1070 } 1071 else { 1072 $self->{uri} = URI->new($self->script_url); 1073 } 1074} 1075 1076sub _generate_error { 1077 my $self = shift; 1078 return undef unless defined $self->{error} and @{$self->{error}}; 1079 1080 my $restrictions = join ',', @{$self->{error}->[1]}; 1081 return $errors{$self->{error}->[0]} . " Restriction: $restrictions Provided: " . $self->{error}->[2]; 1082} 1083 1084sub _validate_file { 1085 my $self = shift; 1086 return undef unless 1087 $self->_validate_max_file_size and 1088 $self->_validate_min_file_size and 1089 $self->_validate_accept_file_types and 1090 $self->_validate_reject_file_types and 1091 $self->_validate_max_width and 1092 $self->_validate_min_width and 1093 $self->_validate_max_height and 1094 $self->_validate_min_height and 1095 $self->_validate_max_number_of_files; 1096 1097 return 1; 1098} 1099 1100sub _save { 1101 my $self = shift; 1102 1103 if(@{$self->scp}) { 1104 $self->_save_scp; 1105 } 1106 else { 1107 $self->_save_local; 1108 } 1109} 1110 1111sub _save_scp { 1112 my $self = shift; 1113 1114 for(@{$self->scp}) { 1115 die "Must provide a host to scp" if $_->{host} eq ''; 1116 1117 $_->{thumbnail_upload_dir} = $_->{upload_dir} if $_->{thumbnail_upload_dir} eq ''; 1118 1119 my $path = $_->{upload_dir} . '/' . $self->filename; 1120 my $thumb_path = $_->{thumbnail_upload_dir} . '/' . $self->thumbnail_filename; 1121 1122 if(($_->{user} ne '' and $_->{public_key} ne '' and $_->{private_key} ne '') or ($_->{user} ne '' and $_->{password} ne '')) { 1123 my $ssh2 = $self->_auth_user($_); 1124 1125 #if it is an image, scp both file and thumbnail 1126 if($self->is_image) { 1127 $ssh2->scp_put($self->{tmp_file_path}, $path); 1128 $ssh2->scp_put($self->{tmp_thumb_path}, $thumb_path); 1129 } 1130 else { 1131 $ssh2->scp_put($self->{tmp_filename}, $path); 1132 } 1133 1134 $ssh2->disconnect; 1135 } 1136 else { 1137 die "Must provide a user and password or user and identity file for connecting to host"; 1138 } 1139 1140 } 1141} 1142 1143sub _auth_user { 1144 my $self = shift; 1145 my ($auth) = @_; 1146 1147 my $ssh2 = Net::SSH2->new; 1148 1149 $ssh2->connect($auth->{host}) or die $!; 1150 1151 #authenticate 1152 if($auth->{user} ne '' and $auth->{public_key} ne '' and $auth->{private_key} ne '') { 1153 $ssh2->auth_publickey($auth->{user},$auth->{public_key},$auth->{private_key}); 1154 } 1155 else { 1156 $ssh2->auth_password($auth->{user},$auth->{password}); 1157 } 1158 1159 unless($ssh2->auth_ok) { 1160 die "error authenticating with remote server"; 1161 } 1162 1163 die "upload directory must be provided with scp hash" if $auth->{upload_dir} eq ''; 1164 1165 return $ssh2; 1166} 1167 1168sub _save_local { 1169 my $self = shift; 1170 1171 #if image 1172 if($self->is_image) { 1173 rename $self->{tmp_file_path}, $self->absolute_filename; 1174 rename $self->{tmp_thumb_path}, $self->absolute_thumbnail_filename; 1175 } 1176 #if non-image with catalyst 1177 elsif(defined $self->ctx) { 1178 if ($self->copy_file) { 1179 $self->{upload}->copy_to($self->absolute_filename); 1180 } else { 1181 $self->{upload}->link_to($self->absolute_filename); 1182 } 1183 } 1184 #if non-image with regular CGI perl 1185 else { 1186 my $io_handle = $self->{fh}->handle; 1187 1188 my $buffer; 1189 open (OUTFILE,'>', $self->absolute_filename); 1190 while (my $bytesread = $io_handle->read($buffer,1024)) { 1191 print OUTFILE $buffer; 1192 } 1193 1194 close OUTFILE; 1195 } 1196} 1197 1198sub _validate_max_file_size { 1199 my $self = shift; 1200 return 1 unless $self->max_file_size; 1201 1202 if($self->{file_size} > $self->max_file_size) { 1203 $self->{error} = ['_validate_max_file_size',[$self->max_file_size],$self->{file_size}]; 1204 return undef; 1205 } 1206 else { 1207 return 1; 1208 } 1209} 1210 1211sub _validate_min_file_size { 1212 my $self = shift; 1213 return 1 unless $self->min_file_size; 1214 1215 if($self->{file_size} < $self->min_file_size) { 1216 $self->{error} = ['_validate_min_file_size',[$self->min_file_size],$self->{file_size}]; 1217 return undef; 1218 } 1219 else { 1220 return 1; 1221 } 1222} 1223 1224sub _validate_accept_file_types { 1225 my $self = shift; 1226 1227 #if accept_file_types is empty, we except all types 1228 #so return true 1229 return 1 unless @{$self->accept_file_types}; 1230 1231 if(grep { $_ eq $self->{file_type} } @{$self->{accept_file_types}}) { 1232 return 1; 1233 } 1234 else { 1235 my $types = join ",", @{$self->accept_file_types}; 1236 $self->{error} = ['_validate_accept_file_types',[$types],$self->{file_type}]; 1237 return undef; 1238 } 1239} 1240 1241sub _validate_reject_file_types { 1242 my $self = shift; 1243 1244 #if reject_file_types is empty, we except all types 1245 #so return true 1246 return 1 unless @{$self->reject_file_types}; 1247 1248 unless(grep { $_ eq $self->{file_type} } @{$self->{reject_file_types}}) { 1249 return 1; 1250 } 1251 else { 1252 my $types = join ",", @{$self->reject_file_types}; 1253 $self->{error} = ['_validate_reject_file_types',[$types],$self->{file_type}]; 1254 return undef; 1255 } 1256} 1257 1258sub _validate_max_width { 1259 my $self = shift; 1260 return 1 unless $self->is_image; 1261 1262 #if set to undef, there's no max_width 1263 return 1 unless $self->max_width; 1264 1265 if($self->{width} > $self->max_width) { 1266 $self->{error} = ['_validate_max_width',[$self->max_width],$self->{width}]; 1267 return undef; 1268 } 1269 else { 1270 return 1; 1271 } 1272} 1273 1274sub _validate_min_width { 1275 my $self = shift; 1276 return 1 unless $self->is_image; 1277 1278 #if set to undef, there's no min_width 1279 return 1 unless $self->min_width; 1280 1281 if($self->{width} < $self->min_width) { 1282 $self->{error} = ['_validate_min_width',[$self->min_width],$self->{width}]; 1283 return undef; 1284 } 1285 else { 1286 return 1; 1287 } 1288} 1289 1290sub _validate_max_height { 1291 my $self = shift; 1292 return 1 unless $self->is_image; 1293 1294 #if set to undef, there's no max_height 1295 return 1 unless $self->max_height; 1296 1297 if($self->{height} > $self->max_height) { 1298 $self->{error} = ['_validate_max_height',[$self->max_height],$self->{height}]; 1299 return undef; 1300 } 1301 else { 1302 return 1; 1303 } 1304} 1305 1306sub _validate_min_height { 1307 my $self = shift; 1308 return 1 unless $self->is_image; 1309 1310 #if set to undef, there's no max_height 1311 return 1 unless $self->min_height; 1312 1313 if($self->{height} < $self->min_height) { 1314 $self->{error} = ['_validate_min_height',[$self->min_height],$self->{height}]; 1315 return undef; 1316 } 1317 else { 1318 return 1; 1319 } 1320} 1321 1322sub _validate_max_number_of_files { 1323 my $self = shift; 1324 return 1 unless $self->max_number_of_files; 1325 1326 if($self->{num_files_in_dir} > $self->max_number_of_files) { 1327 $self->{error} = ['_validate_max_number_of_files',[$self->max_number_of_files],$self->{num_files_in_dir}]; 1328 return undef; 1329 } 1330 else { 1331 return 1; 1332 } 1333} 1334 1335sub _set_file_size { 1336 my $self = shift; 1337 1338 if(defined $self->ctx) { 1339 $self->{file_size} = $self->{upload}->size; 1340 } 1341 else { 1342 $self->{file_size} = -s $self->{upload}; 1343 } 1344 1345 return $self->{file_size}; 1346} 1347 1348sub _set_client_filename { 1349 my $self = shift; 1350 return if defined $self->client_filename; 1351 1352 if(defined $self->ctx) { 1353 $self->client_filename($self->{upload}->filename); 1354 } 1355 else { 1356 $self->client_filename($self->cgi->param($self->field_name)); 1357 } 1358 1359 return $self->client_filename; 1360} 1361 1362sub _set_filename { 1363 my $self = shift; 1364 return if defined $self->filename; 1365 1366 if($self->use_client_filename) { 1367 $self->filename($self->client_filename); 1368 } 1369 else { 1370 my $filename = Data::GUID->new->as_string . $self->filename_salt; 1371 $self->thumbnail_filename($self->thumbnail_prefix . $filename . $self->thumbnail_postfix . '.' . $self->thumbnail_format) unless $self->thumbnail_filename; 1372 1373 if($self->is_image) { 1374 $filename .= '.' . $self->format; 1375 } 1376 else { 1377 #add extension if present 1378 if($self->client_filename =~ qr/.*\.(.*)/) { 1379 $filename .= '.' . $1; 1380 } 1381 } 1382 $self->filename($filename) unless $self->filename; 1383 } 1384 1385 return $self->filename; 1386} 1387 1388sub _set_absolute_filenames { 1389 my $self = shift; 1390 1391 $self->absolute_filename($self->upload_dir . '/' . $self->filename) unless $self->absolute_filename; 1392 $self->absolute_thumbnail_filename($self->thumbnail_upload_dir . '/' . $self->thumbnail_filename) unless $self->absolute_thumbnail_filename; 1393} 1394 1395sub _set_file_type { 1396 my $self = shift; 1397 1398 if(defined $self->ctx) { 1399 $self->{file_type} = $self->{upload}->type; 1400 } 1401 else { 1402 $self->{file_type} = $self->cgi->uploadInfo($self->client_filename)->{'Content-Type'}; 1403 } 1404 1405 return $self->{file_type}; 1406} 1407 1408sub _set_is_image { 1409 my $self = shift; 1410 1411 if($self->process_images and ($self->process_images and ($self->{file_type} eq 'image/jpeg' or $self->{file_type} eq 'image/jpg' or $self->{file_type} eq 'image/png' or $self->{file_type} eq 'image/gif'))) { 1412 $self->{is_image} = 1; 1413 } 1414 else { 1415 $self->{is_image} = 0; 1416 } 1417 1418 return $self->is_image; 1419} 1420 1421sub _set_image_magick { 1422 my $self = shift; 1423 return unless $self->is_image; 1424 1425 #if used in persistent setting, don't recreate object 1426 $self->{image_magick} = Image::Magick->new unless defined $self->{image_magick}; 1427 1428 $self->{image_magick}->Read(file => $self->{fh}); 1429 1430 return $self->{image_magick}; 1431} 1432 1433sub _set_width { 1434 my $self = shift; 1435 return unless $self->is_image; 1436 1437 $self->{width} = $self->{image_magick}->Get('width'); 1438} 1439 1440sub _set_height { 1441 my $self = shift; 1442 return unless $self->is_image; 1443 1444 $self->{height} = $self->{image_magick}->Get('height'); 1445} 1446 1447sub _set_tmp_filename { 1448 my $self = shift; 1449 1450 my $tmp_filename; 1451 if(defined $self->ctx) { 1452 $self->{tmp_filename} = $self->{upload}->tempname; 1453 } 1454 else { 1455 $self->{tmp_filename} = $self->cgi->tmpFileName($self->client_filename); 1456 } 1457} 1458 1459sub _set_upload_obj { 1460 my $self = shift; 1461 1462 if(defined $self->ctx) { 1463 $self->{upload} = $self->ctx->request->upload($self->field_name); 1464 } 1465 else { 1466 $self->{upload} = $self->cgi->upload($self->field_name); 1467 } 1468 1469 return defined $self->{upload}; 1470} 1471 1472sub _set_fh { 1473 my $self = shift; 1474 1475 if(defined $self->ctx) { 1476 $self->{fh} = $self->{upload}->fh; 1477 } 1478 else { 1479 $self->{fh} = $self->{upload}; 1480 } 1481 1482 return $self->{fh}; 1483} 1484 1485sub _set_num_files_in_dir { 1486 my $self = shift; 1487 return unless $self->max_number_of_files; 1488 1489 #DO SCP VERSION 1490 if(@{$self->{scp}}) { 1491 my $max = 0; 1492 for(@{$self->{scp}}) { 1493 my $ssh2 = $self->_auth_user($_); 1494 my $chan = $ssh2->channel(); 1495 $chan->exec('ls -rt ' . $_->{upload_dir} . ' | wc -l'); 1496 my $buffer; 1497 $chan->read($buffer,1024); 1498 ($self->{num_files_in_dir}) = $buffer =~ qr/(\d+)/; 1499 $max = $self->{num_files_in_dir} if $self->{num_files_in_dir} > $max; 1500 } 1501 1502 #set to maximum of hosts because we know if one's over that's too many 1503 $self->{num_files_in_dir} = $max; 1504 } 1505 else { 1506 my $dir = $self->upload_dir; 1507 my @files = <$dir/*>; 1508 $self->{num_files_in_dir} = @files; 1509 } 1510 1511 return $self->{num_files_in_dir}; 1512} 1513 1514sub _get_request_method { 1515 my $self = shift; 1516 1517 my $method = ''; 1518 if(defined $self->ctx) { 1519 $method = $self->ctx->req->method; 1520 } 1521 else { 1522 $method = $self->cgi->request_method; 1523 } 1524 1525 return $method; 1526} 1527 1528sub _set_status { 1529 my $self = shift; 1530 my ($response) = @_; 1531 1532 if(defined $self->ctx) { 1533 $self->ctx->response->status($response); 1534 } 1535 else { 1536 print $self->cgi->header(-status=>$response); 1537 } 1538} 1539 1540sub _set_header { 1541 my $self = shift; 1542 my ($key,$val) = @_; 1543 1544 if(defined $self->ctx) { 1545 $self->ctx->response->header($key => $val); 1546 } 1547 else { 1548 print $self->cgi->header($key,$val); 1549 } 1550} 1551 1552sub _create_thumbnail { 1553 my $self = shift; 1554 1555 my $im = $self->{image_magick}->Clone; 1556 1557 #thumb is added at beginning of tmp_thumb_path as to not clash with the original image file path 1558 my $output = $self->{tmp_thumb_path} = $self->tmp_dir . '/thumb_' . $self->thumbnail_filename; 1559 my $width = $self->thumbnail_width; 1560 my $height = $self->thumbnail_height; 1561 1562 my $density = $self->thumbnail_density || $width . "x" . $height; 1563 my $quality = $self->thumbnail_quality; 1564 my $format = $self->thumbnail_format; 1565 1566 # source image dimensions 1567 my ($o_width, $o_height) = $im->Get('width','height'); 1568 1569 # calculate image dimensions required to fit onto thumbnail 1570 my ($t_width, $t_height, $ratio); 1571 # wider than tall (seems to work...) needs testing 1572 if( $o_width > $o_height ){ 1573 $ratio = $o_width / $o_height; 1574 $t_width = $width; 1575 $t_height = $width / $ratio; 1576 1577 # still won't fit, find the smallest size. 1578 while($t_height > $height){ 1579 $t_height -= $ratio; 1580 $t_width -= 1; 1581 } 1582 } 1583 # taller than wide 1584 elsif( $o_height > $o_width ){ 1585 $ratio = $o_height / $o_width; 1586 $t_height = $height; 1587 $t_width = $height / $ratio; 1588 1589 # still won't fit, find the smallest size. 1590 while($t_width > $width){ 1591 $t_width -= $ratio; 1592 $t_height -= 1; 1593 } 1594 } 1595 # square (fixed suggested by Philip Munt phil@savvyshopper.net.au) 1596 elsif( $o_width == $o_height){ 1597 $ratio = 1; 1598 $t_height = $width; 1599 $t_width = $width; 1600 while (($t_width > $width) or ($t_height > $height)){ 1601 $t_width -= 1; 1602 $t_height -= 1; 1603 } 1604 } 1605 1606 # Create thumbnail 1607 if( defined $im ){ 1608 $im->Resize( width => $t_width, height => $t_height ); 1609 $im->Set( quality => $quality ); 1610 $im->Set( density => $density ); 1611 1612 $self->final_width($t_width); 1613 $self->final_height($t_height); 1614 1615 $im->Write("$format:$output"); 1616 } 1617} 1618 1619sub _create_tmp_image { 1620 my $self = shift; 1621 my $im = $self->{image_magick}; 1622 1623 #main_ is added as to not clash with thumbnail tmp path if thumbnail_prefix = '' and they have the same name 1624 my $output = $self->{tmp_file_path} = $self->tmp_dir . '/main_' . $self->filename; 1625 my $quality = $self->thumbnail_quality; 1626 my $format = $self->thumbnail_format; 1627 1628 if( defined $im ){ 1629 $im->Set( quality => $quality ); 1630 1631 $im->Write("$format:$output"); 1632 1633 $self->final_width($im->Get('width')); 1634 $self->final_height($im->Get('height')); 1635 } 1636} 1637 1638#sub _save_cloud { 1639# my $self = shift; 1640# my $io_handle = $self->{fh}->handle; 1641# 1642# #IF IS IMAGE, MUST UPLOAD BOTH IMAGES 1643# 1644# my $s_contents; 1645# while (my $bytesread = $io_handle->read($buffer,1024)) { 1646# print OUTFILE $buffer; 1647## } 1648# 1649# 1650## while(<FILE>) 1651# { 1652# $s_contents .= $_; 1653## } 1654# 1655## ### we will call this resource whatever comes after the last / 1656# my $s_resourceName; 1657# 1658## if($param->{'path'} =~ /^.*\/(.*)$/) 1659# { 1660# $s_resourceName = $1; 1661## } 1662# else 1663# { 1664# return('fail', "could not parse path: $param->{'path'}"); 1665## } 1666# 1667# ### should we pass these vars ... or look them up? 1668# my $s_user = ''; 1669# my $s_key = ''; 1670## my $s_cdn_uri =''; 1671# 1672# my $ua = LWP::UserAgent->new; 1673# my $req = HTTP::Request->new(GET => 'https://auth.api.rackspacecloud.com/v1.0'); 1674## $req->header('X-Auth-User' => $s_user); 1675# $req->header('X-Auth-Key' => $s_key); 1676# 1677## my $res = $ua->request($req); 1678# 1679# if ($res->is_success) 1680## { 1681# my $s_url = $res->header('X-Storage-Url') . "/container/" . $s_resourceName; 1682# 1683## my $reqPUT = HTTP::Request->new(PUT => $s_url); 1684# $reqPUT->header('X-Auth-Token' => $res->header('X-Auth-Token')); 1685# 1686## $reqPUT->content( $s_contents ); 1687# 1688# my $resPUT = $ua->request($reqPUT); 1689## 1690# if($resPUT->is_success) 1691# { 1692## my $s_returnURI = $s_cdn_uri . "/" . $s_resourceName; 1693# return('pass','passed afaict', $s_returnURI); 1694# } 1695## else 1696# { 1697# my $s_temp = $resPUT->as_string; 1698# $s_temp =~ s/'/\\'/g; 1699## return('fail',"PUT failed with response:$s_temp") 1700# } 1701# } 1702## else 1703# { 1704# my $s_temp = $res->as_string; 1705## $s_temp =~ s/'/\\'/g; 1706# return('fail',"failed with response:$s_temp") 1707# } 1708## } 1709# else 1710# { 1711## return("fail","sorry no file found at $param->{'path'}"); 1712# } 1713#} 1714# 1715##sub _delete_cloud { 1716# my $self = shift; 1717# my $request = HTTP::Request->new( 'DELETE', $self->_url, 1718#Q [ 'X-Auth-Token' => $self->cloudfiles->token ] ); 1719# my $response = $self->cloudfiles->_request($request); 1720# confess 'Object ' . $self->name . ' not found' if $response->code == 404; 1721# confess 'Unknown error' if $response->code != 204; 1722#} 1723 1724# Preloaded methods go here. 1725 17261; 1727__END__ 1728# Below is stub documentation for your module. You'd better edit it! 1729 1730=head1 NAME 1731 1732jQuery::File::Upload - Server-side solution for the L<jQuery File Upload|https://github.com/blueimp/jQuery-File-Upload/> plugin. 1733 1734=head1 SYNOPSIS 1735 1736 use jQuery::File::Upload; 1737 1738 #simplest implementation 1739 my $j_fu = jQuery::File::Upload->new; 1740 $j_fu->handle_request; 1741 $j_fu->print_response; 1742 1743 #alternatively you can call $j_fu->handle_request(1) and this will call print_response for you. 1744 my $j_fu = jQuery::File::Upload->new; 1745 $j_fu->handle_request(1); 1746 1747The above example is the simplest one possible, however it assumes a lot of defaults. 1748 1749=head2 Assumptions 1750 1751=over 4 1752 1753=item default upload directory 1754 1755It is assumed that your files are being uploaded to the current directory of the script that's running, plus '/files'. 1756So if your script is in /home/user/public_html, your files will be uploaded to /home/user/public_html/files. 1757 1758=item default url 1759 1760It is also assumed that the files will be hosted one directory above the running script, plus '/files'. So if the 1761script is located at the url http://www.mydomain.com/upload.cgi, then all files will be assumed to be at the url 1762http://www.mydomain.com/files/file_name. 1763 1764=item default filename 1765 1766Uploaded files are given a name at run time unless you specifically set the filename in the jQuery::File::Upload object. 1767 1768=item same server upload 1769 1770By default, jQuery::File::Upload also assumes that you're meaning to have the uploaded file uploaded to the same server 1771that the script is running on. jQuery::File::Upload also has the ability to SCP files to remote servers, but this is not the default. 1772 1773=item CGI environment 1774 1775By default, jQuery::File::Upload assumes that the script is running in a regular CGI-type environment. However, jQuery::File::Upload 1776also has the ability to work with L<Catalyst> by being passed the context object. 1777 1778=item all files 1779 1780This implementation accepts all types of files. 1781 1782=back 1783 1784=head2 A more complicated example 1785 1786 use jQuery::File::Upload; 1787 1788 my $j_fu = jQuery::File::Upload->new( 1789 scp => [{ 1790 user => 'user', #remote user 1791 public_key => '/home/user/.ssh/id_rsa.pub', #also possible to use password instead of keys 1792 private_key => '/home/user/.ssh/id_rsa', 1793 host => 'mydomain.com', 1794 upload_dir => '/var/www/html/files', #directory that files will be uploaded to 1795 }], 1796 1797 #user validation specifications 1798 max_file_size => 5242880, #max file size is 5mb 1799 min_file_size => 1, 1800 accept_file_types => ['image/jpeg','image/png','image/gif','text/html'], #uploads restricted to these filetypes 1801 max_width => 40000, #max width for images 1802 max_height => 50000, #max height for images 1803 min_width => 1, 1804 min_height => 1, 1805 max_number_of_files => 40, #maximum number of files we can have in our upload directory 1806 ); 1807 1808 1809 $j_fu->handle_request; 1810 $j_fu->print_response; 1811 1812=head2 Getting fancy 1813 1814 use jQuery::File::Upload; 1815 1816 my $j_fu = jQuery::File::Upload->new( 1817 pre_get => sub { 1818 my $j = shift; #jQuery::File::Upload object for current request 1819 #any code in here will be executed before any get requests are handled 1820 #jQuery File Upload makes Get request when the page first loads, so this 1821 #can be useful for prefilling jQuery File Upload with files if you're using 1822 #jQuery File upload with saved data to view/delete/upload more 1823 1824 #generate starting files for jQuery File Upload 1825 $j->generate_output( 1826 [ 1827 { 1828 size => 500000, 1829 filename => 'my_image.jpeg', 1830 image => 'y', #need to let jQuery::File::Upload know this is an image 1831 #or else thumbnails won't be deleted 1832 }, 1833 { 1834 size => 500000, 1835 filename => 'my_other_image.jpeg', 1836 image => 'y', 1837 }, 1838 ] 1839 ); 1840 1841 #The above makes assumptions yet again. It generates the url based on the defaults, unless 1842 #you provide below the upload_url_base. 1843 }, 1844 pre_delete => sub { 1845 my $j = shift; 1846 1847 #here you can do something with the information in the params of the delete_url 1848 #you can set your own meaningful delete_params (see below) 1849 #NOTE: delete_urls always have the 'filename' param, so that might be enough for your needs 1850 my $id = param->('id') 1851 1852 #DELETE FROM table WHERE id=$id 1853 #etc. 1854 }, 1855 post_post => sub { 1856 my $j = shift; 1857 #do some stuff here after post (image is uploaded) 1858 #possibly save information about image in a database to keep track of it? 1859 #you can call any methods now to get useful info: 1860 1861 #INSERT INTO table (name,is_image,width,height) VALUES($j->filename,$j->is_image,$j->final_width,$j->final_height) 1862 #etc 1863 }, 1864 delete_params => ['key1','val1','key2','val2'], 1865 #this will add these key value pairs as 1866 #params on the delete_url that is generated 1867 #for each image. This could be useful if you 1868 #kept track of these files in a database and wanted to 1869 #delete them from the database when they are deleted. 1870 #then delete_params could be ['id',unique_db_identifier] 1871 #then you could check for the param 'id' in pre_delete 1872 #and delete the file from your DB 1873 ); 1874 1875 1876 #N.B. All of the above can also be set with the getter/setter methods 1877 1878 $j_fu->handle_request(1); #when passed a one, will call print_response for you 1879 1880=head1 DESCRIPTION 1881 1882jQuery::File::Upload makes integrating server-side with the L<jQuery File Upload|https://github.com/blueimp/jQuery-File-Upload/> plugin simple. 1883It provides many features, such as: 1884 1885=over 4 1886 1887=item 1 1888 1889the ability to SCP file uploads to remote servers 1890 1891=item 2 1892 1893the ability to provide your own functions to add to how each request is handled before the request and after the request 1894 1895=item 3 1896 1897options to validate the uploaded files server-side 1898 1899=item 4 1900 1901automatically generates thumbnails if the file is an image 1902 1903=item 5 1904 1905see below for everything you can do with jQuery::File::Upload 1906 1907=back 1908 1909The location of the script should be where L<jQuery File Upload|https://github.com/blueimp/jQuery-File-Upload/> is 1910told to upload to. 1911 1912=head1 METHODS 1913 1914=head2 Getters/Setters 1915 1916=head3 new 1917 1918Any of the below getters/setters can be passed into new as options. 1919 1920 my $j_fu = jQuery::File::Upload->new(option=>val,option2=>val2,...); 1921 1922=head3 upload_dir 1923 1924 $j_fu->upload_dir('/home/user/public_html/files'); 1925 1926Sets the upload directory if saving files locally. Should not end with a slash. 1927The default is the current directory of the running script with '/files' added to 1928the end: 1929 1930 /home/user/public_html/upload.cgi 1931 1932yields: 1933 1934 /home/user/public_html/files 1935 1936When using jQuery::File::Upload under normal CGI, it should have 1937no problem generating this default upload directory if that's what you want. 1938However, if you are using L<Catalyst>, depending on how you're running L<Catalyst> 1939(i.e. mod_perl, fastcgi, etc.) the generated default might be kind of strange. 1940So if you are using L<Catalyst> and you want to upload to the same server 1941that jQuery::File::Upload is running on, it's best to just manually set this. 1942Make sure that the user running your script can write to the directory you 1943specify. 1944 1945=head3 thumbnail_upload_dir 1946 1947 $j_fu->thumbnail_upload_dir('/home/user/public_html/files/thumbs'); 1948 1949This can be used to set the upload directory form thumbnails. The default 1950is L<upload_dir|/"upload_dir">. If you change this, that will make thumbnails 1951have a different base url than L<upload_url_base|/"upload_url_base">. Make 1952sure to change L<thumbnail_url_base|/"thumbnail_url_base"> to match this accordingly. 1953If you would like images and thumbnails to have the same name but just be in 1954different directories, make sure you set L<thumbnail_prefix|/"thumbnail_prefix"> 1955to ''. This should not end with a slash. 1956Make sure that the user running your script can write to the directory you 1957specify. 1958 1959=head3 upload_url_base 1960 1961 $j_fu->upload_url_base('http://www.mydomain.com/files'); 1962 1963Sets the url base for files. Should not end with a slash. 1964The default is the current directory of the running script 1965with '/files' added to the end: 1966 1967 http://www.mydomain.com/upload.cgi 1968 1969yields: 1970 1971 http://www.mydomain.com/files 1972 1973Which means that a file url would look like this: 1974 1975 http://www.mydomain.com/files/file.txt 1976 1977=head3 thumbnail_url_base 1978 1979 $j_fu->thumbnail_url_base('http://www.mydomain.com/files/thumbs'); 1980 1981Sets the url base for thumbnails. Should not end with a slash. 1982The default is L<upload_url_base|/"upload_url_base">. 1983Resulting thumbnail urls would look like: 1984 1985 http://www.mydomain.com/files/thumbs/thumb_image.jpg 1986 1987However, if L<thumbnail_relative_url_base|/"thumbnail_relative_url_base"> 1988is set, the default will be the current url with the thumbnail 1989relative base at the end. 1990 1991=head3 relative_url_path 1992 1993 $j_fu->relative_url_path('/files'); 1994 1995This sets the relative url path for your files relative to the directory 1996your script is currently running in. For example: 1997 1998 http://www.mydomain.com/upload.cgi 1999 2000yields: 2001 2002 http://www.mydomain.com/files 2003 2004and then all files will go after /files. The default for this is /files, 2005which is why upload_url_base has the default /files at the end. If 2006your location for the images is not relative, i.e. it is located 2007at a different domain, then just set L<upload_url_base|/"upload_url_base"> 2008to get the url_base you want. There should not be 2009a slash at the end. 2010 2011=head3 thumbnail_relative_url_path 2012 2013 $j_fu->thumbnail_relative_url_path('/files/thumbs'); 2014 2015This sets the thumbnail relative url path for your files relative to the directory 2016your script is currently running in. For example: 2017 2018 http://www.mydomain.com/upload.cgi 2019 2020yields: 2021 2022 http://www.mydomain.com/files/thumbs 2023 2024and then all thumbnails will go after /files/thumbs. The default for this is nothing, 2025so then the thumbnail_url will just fall back on whatever the value of 2026L<upload_url_base|/"upload_url_base"> is. 2027If your location for thumbnail images is not relative, i.e. it is located 2028at a different domain, then just set L<thumbnail_url_base|/"thumbnail_url_base"> 2029to get the url_base you want. There should not be 2030a slash at the end. 2031 2032=head3 relative_to_host 2033 2034 $j_fu->relative_to_host(1); 2035 2036If set to 1, this will make L<relative_url_path|/"relative_url_path"> and 2037L<thumbnail_relative_url_path|/"thumbnail_relative_url_path"> be relative to 2038the host of the script url. For example: 2039 2040 http://www.mydomain.com/folder/upload.cgi 2041 2042With a L<relative_url_path|/"relative_url_path"> '/files' would yield: 2043 2044 http://www.mydomain.com/files 2045 2046Whereas by default L<relative_url_path|/"relative_url_path"> and 2047L<thumbnail_relative_url_path|/"thumbnail_relative_url_path"> are 2048relative to the folder the upload script is running in. 2049 2050If you use this option, make sure to set L<upload_dir|/"upload_dir"> 2051(and/or L<thumbnail_upload_dir|/"thumbnail_upload_dir"> if necessary) 2052since jQuery::File::Upload can no longer do a relative path 2053for saving the file. 2054 2055Default is undef. 2056 2057=head3 field_name 2058 2059 $j_fu->field_name('files[]'); 2060 2061This is the name of the jQuery File Uploader client side. 2062The default is files[], as this is the jQuery File Upload 2063plugin's default. 2064 2065=head3 ctx 2066 2067 $j_fu->ctx($c); 2068 2069This is meant to set the L<Catalyst> context object if you are using 2070this plugin with L<Catalyst>. The default is to not use this. 2071 2072=head3 cgi 2073 2074 $j_fu->cgi(CGI->new); 2075 2076This should be used mostly internally by jQuery::File::Upload 2077(assuming you haven't passed in ctx). 2078It is just the CGI object that the module uses, however if you already 2079have one you could pass it in. 2080 2081=head3 should_delete 2082 2083 $j_fu->should_delete(1) 2084 2085This is used to decide whether to actually delete the files when jQuery::File::Upload 2086receives a DELETE request. The default is to delete, however this could be useful 2087if you wanted to maybe just mark the field as deleted in your database (using L<pre_delete|/"pre_delete">) 2088and then actually physically 2089remove it with your own clean up script later. The benefit to this could be that 2090if you are SCPing the files to a remote server, perhaps issuing the remote commands 2091to delete these files is something that seems to costly to you. 2092 2093=head3 scp 2094 2095 $j_fu->scp([{ 2096 host => 'media.mydomain.com', 2097 user => 'user', 2098 public_key => '/home/user/.ssh/id_rsa.pub', 2099 private_key => '/home/user/.ssh/id_rsa', 2100 password => 'pass', #if keys are present, you do not need password 2101 upload_dir => '/my/remote/dir', 2102 }]); 2103 2104This method takes in an arrayref of hashrefs, where each hashref is a remote host you would like to SCP the files to. 2105SCPing the uploaded files to remote hosts could be useful if say you hosted your images on a different server 2106than the one doing the uploading. 2107 2108=head4 SCP OPTIONS 2109 2110=over 4 2111 2112=item 2113 2114host (REQUIRED) - the remote host you want to scp the files to, i.e. 127.0.0.1 or media.mydomain.com 2115 2116=item 2117 2118user (REQUIRED) - used to identify the user to remote server 2119 2120=item 2121 2122public_key & private_key - used to make secure connection. Not needed if password is given. 2123 2124=item 2125 2126password - used along with user to authenticate with remote server. Not needed if keys are supplied. 2127 2128=item 2129 2130upload_dir (REQUIRED) - the directory you want to scp to on the remote server. Should not end with a slash 2131 2132=item 2133 2134thumbnail_upload_dir - Will default to upload_dir. You only need to provide this if your thumbnails are stored in a different directory than regular images. Should not end with a slash 2135 2136=back 2137 2138You can check L<Net::SSH2> for more information on connecting to the remote server. 2139 2140=head3 max_file_size 2141 2142 $j_fu->max_file_size(1024); 2143 2144Sets the max file size in bytes. By default there is no max file size. 2145 2146=head3 min_file_size 2147 2148 $j_fu->min_file_size(1); 2149 2150Sets the minimum file size in bytes. Default minimum is 1 byte. to disable a minimum file size, you can set this to undef or 0. 2151 2152=head3 accept_file_types 2153 2154 $j_fu->accept_file_types(['image/jpeg','image/png','image/gif','text/html']); 2155 2156Sets what file types are allowed to be uploaded. By default, all file types are allowed. 2157File types should be in the format of the Content-Type header sent on requests. 2158 2159=head3 reject_file_types 2160 2161 #None of these types are allowed. 2162 $j_fu->reject_file_types(['image/jpeg','image/png','image/gif','text/html']); 2163 2164Sets what file types are NOT allowed to be uploaded. By default, all file types are allowed. 2165File types should be in the format of the Content-Type header sent on requests. 2166 2167=head3 require_image 2168 2169 $j_fu->require_image(1); 2170 2171If set to 1, it requires that all uploads must be an image. Setting this is equivalent 2172to calling: 2173 2174 $j_fu->accept_file_types(['image/jpeg','image/jpg','image/png','image/gif']); 2175 2176Default is undef. 2177 2178=head3 delete_params 2179 2180 $j_fu->delete_params(['key1','val1','key2','val2']); 2181 2182Sets the keys and values of the params added to the delete_url. 2183This can be useful when used with L<pre_delete|/"pre_delete">, 2184because if you are keeping track of these files in a database, 2185you can add unique identifiers to the params so that in L<pre_delete|/"pre_delete"> 2186you can get these unique identifiers and use them to remove or edit the file 2187in the databse. By default filename will also be a param unless you 2188set the delete_url manually. 2189 2190=head3 delete_url 2191 2192 $j_fu->delete_url('http://www.mydomain.com/upload.cgi?filename=file.jpg'); 2193 2194This can be used to set the delete_url that will be requested when 2195a user deletes a file. However, it is recommended that you do not 2196set this manually and rather use L<delete_params|/"delete_params"> 2197if you want to add your own params to the delete_url. 2198 2199=head3 thumbnail_width 2200 2201 $j_fu->thumbnail_width(80); 2202 2203This sets the width for the thumbnail that will be created if the 2204file is an image. Default is 80. 2205 2206=head3 thumbnail_height 2207 2208 $j_fu->thumbnail_height(80); 2209 2210This sets the height for the thumbnail that will be created if the 2211file is an image. Default is 80. 2212 2213=head3 thumbnail_quality 2214 2215 $j_fu->thumbnail_quality(70); 2216 2217This sets the quality of the thumbnail image. Default is 70 and it 2218can be on a scale of 0-100. See L<Image::Magick> for more information. 2219 2220=head3 thumbnail_format 2221 2222 $j_fu->thumbnail_format('jpg'); 2223 2224Sets the format for the generated thumbnail. Can be jpg, png, or gif. 2225See L<Image::Magick> for more information. Defaults to jpg. 2226 2227=head3 thumbnail_density 2228 2229 $j_fu->thumbnail_density('80x80'); 2230 2231Sets the density for the generated thumbnail. Default is width x height. 2232See L<Image::Magick> for more information. 2233 2234=head3 thumbnail_prefix 2235 2236 $j_fu->thumbnail_prefix('thumb_'); 2237 2238Added before the image filename to create the thumbnail unique filename. 2239Default is 'thumb_'. 2240 2241=head3 thumbnail_postfix 2242 2243 $j_fu->thumbnail_postfix('_thumb'); 2244 2245Added after the image filename to create the thumbnail unique filename. 2246Default is ''. 2247 2248=head3 thumbnail_final_width 2249 2250 my $final_width = $j_fu->thumbnail_final_width; 2251 2252Because the thumbnails are scaled proportionally, the thumbnail width 2253may not be what you orignally suggested. This gets you the final width. 2254 2255=head3 thumbnail_final_height 2256 2257 my $final_height = $j_fu->thumbnail_final_height; 2258 2259Because the thumbnails are scaled proportionally, the thumbnail height 2260may not be what you orignally suggested. This gets you the final height. 2261 2262=head3 quality 2263 2264 $j_fu->quality(70); 2265 2266This sets the quality of the uploaded image. Default is 70 and it 2267can be on a scale of 0-100. See L<Image::Magick> for more information. 2268 2269=head3 process_images 2270 2271 $j_fu->process_images(1); 2272 2273Create thumbnails for uploaded image files when set to 1. When set to undef, L<jQuery::File::Upload> will skip creating 2274thumbnails even when the uploaded file is an image. Images are simply treated like any other file. The default is 1. 2275 2276=head3 format 2277 2278 $j_fu->format('jpg'); 2279 2280Sets the format for the generated thumbnail. Can be jpg,png, or gif. 2281See L<Image::Magick> for more information. Defaults to jpg. 2282 2283=head3 final_width 2284 2285 my $final_width = $j_fu->final_width; 2286 2287Returns the final width of the uploaded image. 2288 2289=head3 final_height 2290 2291 my $final_height = $j_fu->final_height; 2292 2293Returns the final height of the uploaded image. 2294 2295=head3 max_width 2296 2297 $j_fu->max_width(10000); 2298 2299Sets the maximum width of uploaded images. Will return an error to browser if not 2300valid. Default is any width. 2301 2302=head3 max_height 2303 2304 $j_fu->max_height(10000); 2305 2306Sets the maximum height of uploaded images. Will return an error to browser if not 2307valid. Default is any height. 2308 2309=head3 min_width 2310 2311 $j_fu->min_width(10000); 2312 2313Sets the minimum width of uploaded images. Will return an error to browser if not 2314valid. Default is 1. 2315 2316=head3 min_height 2317 2318 $j_fu->min_height(10000); 2319 2320Sets the minimum height of uploaded images. Will return an error to browser if not 2321valid. Default is 1. 2322 2323=head3 max_number_of_files 2324 2325 $j_fu->max_number_of_files(20); 2326 2327Sets the maximum number of files the upload directory can contain. Returns an error 2328to the browser if number is reached. Default is any number of files. If you have 2329listed multiple remote directories, the maximum file count out of all of these directories 2330is what will be used. 2331 2332=head3 filename 2333 2334 my $filename = $j_fu->filename; 2335 2336Returns the resulting filename after processing the request. 2337 2338 $j_fu->filename('my_name.txt'); 2339 2340You can also set the filename to use for this request before you call 2341L<handle_request|/"handle_request">. However, unless you're sure 2342that you are going to give the file a unique name, you should 2343just let jQuery::File::Upload generate the filename. Please note 2344that if you choose your own filename, you do have to manually set 2345L<thumbnail_filename|/"thumbnail_filename"> 2346 2347=head3 absolute_filename 2348 2349 my $absolute_filename = $j_fu->absolute_filename; 2350 2351Returns the absolute filename of the file on the server. 2352You can also set this manually if you would like, or jQuery::File::Upload 2353will generate it for you. 2354 2355=head3 thumbnail_filename 2356 2357 $j_fu->filename('my_name.txt'); 2358 2359You can also set the thumbnail_filename to use for this request before you call 2360L<handle_request|/"handle_request">. However, unless you're sure 2361that you are going to give the file a unique name, you should 2362just let jQuery::File::Upload generate the filename. 2363 2364=head3 absolute_thumbnail_filename 2365 2366 my $absolute_filename = $j_fu->absolute_thumbnail_filename; 2367 2368Returns the absolute filename of the thumbnail image on the server. 2369You can also set this manually if you would like, or jQuery::File::Upload 2370will generate it for you. 2371 2372=head3 client_filename 2373 2374 my $client_filename = $j_fu->client_filename; 2375 2376Returns the filename of the file as it was named by the user. 2377 2378=head3 show_client_filename 2379 2380 $j_fu->show_client_filename(1); 2381 2382This can be used to set whether jQuery::File::Upload shows the user the name 2383of the file as it looked when they uploaded, or the new name of the file. 2384When set to true, the user will see the file as it was named on their computer. 2385The default is true, and this is recommended because typically the user's 2386filename will look better than the unique one that jQuery::File::Upload generates 2387for you. 2388 2389=head3 use_client_filename 2390 2391 $j_fu->use_client_filename(0); 2392 2393If this is set to true, jQuery::File::Upload will use 2394the user's name for the file when saving it. However, this 2395is not recommended because the user could have two files named 2396the same thing that could overwrite one another, and same scenario 2397between two different users. It is best to let jQuery::File::Upload 2398generate the filenames to save with because these are much more 2399likely to be unique. Another reason not to use client filenames 2400is that it is possible that they could have invalid characters in them 2401such as spaces which will prevent a url from loading. 2402 2403=head3 filename_salt 2404 2405 $j_fu->filename_salt('_i_love_the_circus'); 2406 2407Anything added here will be appended to the end of the filename. 2408This is meant to be used if you want to guarantee uniqueness of image 2409names, i.e. you could use a user id at the end to greatly lessen the chance 2410of duplicate filenames. Default is nothing. 2411 2412=head3 copy_file 2413 2414 $j_fu->copy_file(undef); 2415 2416Performs a copy instead of a link from the temporary directory to the upload directory. 2417This might be useful if you are using Windows share that can't handle links. 2418The default is undef and thus L<jQuery::File::Upload> will use links. 2419 2420=head3 tmp_dir 2421 2422 $j_fu->tmp_dir('/tmp'); 2423 2424The provided directory will be used to store temporary files such as images. 2425Make sure that the user the script is running under has permission to create 2426and write to files in the tmp_dir. Also, there should be no slash at the end. 2427Default is /tmp. 2428 2429=head3 script_url 2430 2431 $j_fu->script_url('http://www.mydomain.com/upload.cgi'); 2432 2433This can be used to set the url of the script that jQuery::File::Upload is 2434running under. jQuery::File::Upload then uses this value to generate 2435other parts of the output. jQuery::File::Upload in most cases is able 2436to figure this out on its own, however if you are experiencing issues 2437with things such as url generation, try setting this manually. 2438 2439=head3 data 2440 2441 $j_fu->data({ 2442 dbh => $dbh, 2443 my_var = $var, 2444 arr = [], 2445 self => $self, #maybe useful for Catalyst 2446 }); 2447 2448This method can be populated with whatever you like. Its purpose is 2449if you need to get access to other data in one of your 2450L</PRE/POST REQUEST METHODS>. This way you 2451can access any outside data you need by calling L<data|/"data"> on 2452the jQuery::File::Upload object that you are passed. However, keep in mind 2453that if you are using L<Catalyst>, you will have access to the context 2454object via the jQuery::File::Upload object that is passed in, and this 2455would be an equally good place to store/retrieve data that you need. 2456 2457=head2 JUST GETTERS 2458 2459=head3 output 2460 2461 my $output = $j_fu->output; 2462 2463Returns the JSON output that will be printed to the browser. 2464Unless you really feel you need the JSON, it's usually just easier to 2465call L<print_response|/"print_response"> as this prints out 2466the header and the JSON for you (or alternatively call L<handle_response|/"handle_response> 2467and pass it a 1 so that it will call L<print_response|/"print_response"> for you. 2468 2469=head3 url 2470 2471 my $file_url = $j_fu->url; 2472 2473This returns the resulting url of the file. 2474 2475=head3 thumbnail_url 2476 2477 my $thumbnail_url = $j_fu->thumbnail_url; 2478 2479This returns the resulting thumbnail url of the image. 2480 2481=head3 is_image 2482 2483 my $is_image = $j_fu->is_image; 2484 2485Returns whether or not the uploaded file was an image. 2486This should be called after handle_request or in 2487L<post_post|/"post_post">. 2488 2489=head3 size 2490 2491 my $size = $j_fu->size; 2492 2493Returns the size of the uploaded file. 2494This should be called after handle_request or in 2495L<post_post|/"post_post">. 2496 2497 2498=head2 OTHER METHODS 2499 2500=head3 print_response 2501 2502 $j_fu->print_response; 2503 2504Should be called after L<handle_request|/"handle_request">. 2505Prints out header and JSON back to browser. Called for 2506convenience by L<handle_request|/"handle_request"> if 2507L<handle_request|/"handle_request"> is passed a 1. 2508 2509=head3 handle_request 2510 2511 $j_fu->handle_request; 2512 2513Called to handle one of 'GET','POST', or 'DELETE' requests. 2514If passed a 1, will also call L<print_response|/"print_response"> 2515after it's finished. 2516 2517=head3 generate_output 2518 2519 $j_fu->generate_output([{ 2520 image => 'y', #or 'n' 2521 filename => 'my_cool_pic.jpg', 2522 size => 1024, 2523 }]); 2524 2525This should be used in conjuction with L<pre_get|/"pre_get"> 2526to populate jQuery File Upload with files on page load. It takes in 2527an arrayref of hashrefs, where each hashref is a file. After this 2528method is called, you will need to call L<print_response|/"print_response"> 2529or L<handle_request|/"handle_request"> with a 1 to print out the JSON. 2530 2531=head4 GENERATE_OUTPUT OPTIONS 2532 2533=over 4 2534 2535=item 2536 2537filename (REQUIRED) - name of the file 2538 2539=item 2540 2541size (REQUIRED) - size in bytes 2542 2543=item 2544 2545image - 'y' or 'n'. Necessary if file is image and you would like thumbnail to be deleted with file. Also, needed if you want thumbnail to be displayed by jQuery File Upload 2546 2547=item 2548 2549name - name that will be displayed to client as the filename. If not provided, defaults to filename. Can be 2550used well with client_filename to make filename's look prettier client-side. 2551 2552=item 2553 2554thumbnail_filename - filename for thumbnail. jQuery::File::Upload will generate the thumbnail_filename based 2555on the filename and other factors (such as L<upload_url_base|/"upload_url_base">) if you don't set this. 2556 2557=item 2558 2559url - url used for file. If not provided, will be generated with filename and other defaults. 2560 2561=item 2562 2563thumbnail_url - url used for thumbnail. If not provided, will be generated with other defaults. 2564 2565=item 2566 2567delete_url - url that will be called by L<jQuery File Upload|https://github.com/blueimp/jQuery-File-Upload/> to 2568delete the file. It's better to just let jQuery::File::Upload generate this and use L<delete_params|/"delete_params"> 2569if you want to set your own parameters for the delete url. 2570 2571=item 2572 2573delete_params - The format of this is just like L<delete_params|/"delete_params">. It takes [key,value] pairs. 2574Any values here will be added in addition to any global L<delete_params|/"delete_params"> that you set. 2575 2576=item 2577 2578error - can be used to supply an error for a file (although I don't really know why you would use this...) 2579 2580=back 2581 2582Note that jQuery::File::Upload will generate urls and such 2583based upon things given here (like filename) and other 2584options such as L<upload_url_base|/"upload_url_base">. 2585 2586=head2 PRE/POST REQUEST METHODS 2587 2588N.B. The following functions are all passed a jQuery::File::Upload object. And they 2589can be passed into L<new|/"new"> as options. 2590 2591Also, note that since all of these user-defined methods are passed the jQuery::File::Upload object, 2592if you are using L<Catalyst> you can just call the L<ctx|/"ctx"> method to get anything 2593stored via your context object. For L<Catalyst> users, this makes this a practical (and possibly better) 2594alternative to the provided L<data|/"data"> method. 2595 2596=head3 pre_delete 2597 2598 $j_fu->pre_delete(sub { my $j_fu = shift }); 2599 2600or 2601 2602 $j_fu->pre_delete(\&mysub); 2603 2604pre_delete will be called before a delete request is handled. 2605This can be useful if you want to mark a file as deleted in your 2606database. Also, you can use this along with L<delete_params|/"delete_params"> 2607to set unique identifiers (such as an id for the file or the primary key) so that you can 2608find the file in your database easier to perform whatever operations 2609you want to on it. B<Note:> This will be called even if 2610L<should_delete|/"should_delete"> is set to false. 2611If your pre_delete returns a value, this will be interpreted as an error 2612message and the delete call will be terminated and will return the error. 2613For example: 2614 2615 $j_fu->pre_delete(sub { 2616 return 'You cannot delete this file.'; #file will not be deleted 2617 }); 2618 2619=head3 post_delete 2620 2621 $j_fu->post_delete(sub { my $j_fu = shift }); 2622 2623or 2624 2625 $j_fu->post_delete(\&mysub); 2626 2627post_delete will be called after a delete request is handled. 2628B<Note:> This will not be called if 2629L<should_delete|/"should_delete"> is set to false. 2630 2631=head3 pre_post 2632 2633 $j_fu->pre_post(sub { my $j_fu = shift }); 2634 2635or 2636 2637 $j_fu->pre_post(\&mysub); 2638 2639pre_post will be called before a post request is handled. 2640POST requests are what happen when jQuery File Upload uploads your file. 2641If your pre_post returns a value, this will be interpreted as an error 2642message and the post call will be terminated and will return the error. 2643For example: 2644 2645 $j_fu->pre_post(sub { 2646 return 'You have too many files.'; #file will not be uploaded 2647 }); 2648 2649=head3 post_post 2650 2651 $j_fu->post_post(sub { my $j_fu = shift }); 2652 2653or 2654 2655 $j_fu->post_post(\&mysub); 2656 2657post_post will be called after a post request is handled. 2658This can be useful if you want to keep track of the file 2659that was just uploaded by recording it in a database. 2660You can use the jQuery::File::Upload object that 2661is passed in to get information about the file that you would like 2662to store in the databse. Later on you can use this stored 2663information about the files to prepopulate a jQuery File Upload 2664form with files you already have by preloading the form by using 2665L<pre_get|/"pre_get">. 2666 2667=head3 pre_get 2668 2669 $j_fu->pre_get(sub { my $j_fu = shift }); 2670 2671or 2672 2673 $j_fu->pre_get(\&mysub); 2674 2675pre_get will be called before a get request is handled. 2676Get requests happen on page load to see if there are any 2677files to prepopulate the form with. This method can 2678be useful to prepopulate the jQuery File Upload form 2679by combining saved information about the files you want to 2680load and using L<generate_output|/"generate_output"> to 2681prepare the output that you would like to send to the 2682jQuery File Upload form. 2683 2684=head3 post_get 2685 2686 $j_fu->post_get(sub { my $j_fu = shift }); 2687 2688or 2689 2690 $j_fu->post_get(\&mysub); 2691 2692post_get will be called after a get request is handled. 2693 2694=head2 EXPORT 2695 2696None by default. 2697 2698=head1 Catalyst Performance - Persistent jQuery::File::Upload 2699 2700A jQuery::File::Upload object shouldn't be too expensive to create, however 2701if you'd like to only create the object once you could create it as an 2702L<Moose> attribute to the class: 2703 2704 use jQuery::File::Upload; 2705 has 'j_uf' => (isa => 'jQuery::File::Upload', is => 'rw', 2706 lazy => 0, default => sub { jQuery::File::Upload->new } ); 2707 2708However, if you do this it is possible that you could run into issues 2709with values of the jQuery::File::Upload object that were not cleared 2710messing with the current request. The _clear method is called before 2711every L<handle_request|/"handle_request"> which clears the values of 2712the jQuery::File::Upload object, but it's possible I may have 2713missed something. 2714 2715=head1 SEE ALSO 2716 2717=over 4 2718 2719=item 2720 2721L<CGI> 2722 2723=item 2724 2725L<JSON::XS> 2726 2727=item 2728 2729L<Net::SSH2> 2730 2731=item 2732 2733L<Net::SSH2::SFTP> 2734 2735=item 2736 2737L<Image::Magick> 2738 2739=item 2740 2741L<Cwd> 2742 2743=item 2744 2745L<Digest::MD5> 2746 2747=item 2748 2749L<URI> 2750 2751=item 2752 2753L<jQuery File Upload|https://github.com/blueimp/jQuery-File-Upload/> 2754 2755=back 2756 2757=head1 AUTHOR 2758 2759Adam Hopkins, E<lt>srchulo@cpan.org> 2760 2761=head1 Bugs 2762 2763I haven't tested this too thoroughly beyond my needs, so it is possible 2764that I have missed something. If I have, please feel free to submit a bug 2765to the bug tracker, and you can send me an email letting me know that you 2766submitted a bug if you want me to see it sooner :) 2767 2768=head1 COPYRIGHT AND LICENSE 2769 2770Copyright (C) 2013 by Adam Hopkins 2771 2772This library is free software; you can redistribute it and/or modify 2773it under the same terms as Perl itself, either Perl version 5.8.8 or, 2774at your option, any later version of Perl 5 you may have available. 2775 2776=cut 2777