-
Notifications
You must be signed in to change notification settings - Fork 2
/
Sendfile.pm
131 lines (93 loc) · 3.23 KB
/
Sendfile.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package Catalyst::TraitFor::Controller::Sendfile;
use Moose::Role;
use MooseX::Types::Path::Class qw/ File /;
use MooseX::Types::Moose qw/ Str /;
use MIME::Types;
use Method::Signatures::Simple;
use namespace::autoclean;
# ABSTRACT: convenience method to send files with X-Sendfile, X-Accel-Redirect, ...
=head1 SYNOPSIS
package MyApp::Controller::Foo;
use Moose;
use Path::Class qw/ file /;
use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller' }
with 'Catalyst::TraitFor::Controller::Sendfile';
__PACKAGE__->config(sendfile_header => 'X-Sendfile');
sub some_action : Local {
my ($self, $c) = @_;
$self->sendfile($c, file(qw/ path to file/), 'image/jpeg');
}
=head1 DESCRIPTION
If you want to deliver files using headers like 'X-Sendfile'
or 'X-Accel-Redirect' you can apply this trait and use its convenience method sendfile.
=head1 ATTRIBUTES
=head2 sendfile_header
name of the Sendfile header. Defaults to X-Sendfile (apache mod_sendfile and lighttpd),
or should be changed to 'X-Accel-Redirect' for nginx
=cut
has sendfile_header => (
is => 'ro',
isa => Str,
default => 'X-Sendfile',
);
=head2 _mime_types
data structure used to look up the mime type for file extensions
=cut
has '_mime_types' => (
is => 'ro',
default => sub {
my $mime = MIME::Types->new( only_complete => 1 );
$mime->create_type_index;
$mime;
}
);
=head1 METHODS
=head2 set_content_type_for_file
=cut
method set_content_type_for_file ($c, $file, $content_type) {
if (!$content_type) {
my ($ext) = $file->basename =~ /\.(.+?)$/;
die "Could not find file extension. (" . $file->basename . ")"
unless defined $ext;
$content_type = $self->_mime_types->mimeTypeOf($ext);
die "No content-type found for '$ext'"
unless defined $content_type;
}
die "No content-type found. (" . $file->basename . ")"
unless defined $content_type;
$c->res->content_type($content_type);
}
=head2 sendfile
You call sendfile with $c, Path::Class::File object and an optional content_type.
The file path can't be seen by the client.
Your webserver should check if the 'X-Sendfile' header is set and if so deliver the file.
If you do not define a content_type it will be guessed by the file extension.
=cut
method sendfile ($c, $file, $content_type) {
die "No file supplied to sendfile with" unless $file;
my $file_ob = to_File($file);
die "Not supplied with a Path::Class::File or something that can be coerced to be one ($file)"
unless $file = $file_ob;
$content_type = $self->set_content_type_for_file($c, $file, $content_type);
my $engine = $ENV{CATALYST_ENGINE} || 'HTTP';
# Catalyst development server
if ( $engine =~ /^HTTP/ ) {
if ( $file->stat && -f _ && -r _ ) {
$c->res->body( $file->openr );
$c->res->content_length( $file->stat->size );
}
}
# Deployment with FastCGI
elsif ( $engine eq 'FastCGI' ) {
$c->res->header($self->sendfile_header, $file);
$c->res->body( '' );
}
# unknown engine
else {
die "Unknown engine: " . $engine;
}
$c->res->status(200);
$c->detach;
}
1;