[Catalyst] Re: Content Disposition filename

Bill Moseley moseley at hank.org
Tue Dec 3 14:44:18 GMT 2013

On Tue, Nov 19, 2013 at 10:32 AM, Bill Moseley <moseley at hank.org> wrote:

> Anyone aware of a good, portable way in Perl to encode the filename in a
> Content-Disposition header? I would like to support UTF8 filenames, but
> support in browsers is unclear (if not changing).
> Is this complexity something that the Catalyst framework should handle?
> It's one of those areas where it's easy to get wrong (I can see many
> different approaches in our own code).
> http://greenbytes.de/tech/tc2231/
> http://stackoverflow.com/questions/93551/how-to-encode-the-filename-param=

I have no idea what the client can accept or what its OS uses as a
path-separator, and I don't want to go down the client-sniffing path,

I have a user-supplied character string that I want to use as the filename,
which I have to assume can contain any unicode character since it's
user-supplied data.

>From my limited tests it seems most modern browsers are supporting the
"filename*" extension.   Each browser does some special handling (like
replacing the path-separator, or adding a file extension based on
content-type if no file extension is in the filename).

All I want to do is make valid HTTP headers and let the client decide how
to handle it, but also provide a usable filename (not just underscores, for

So, all I'm after is to make this valid markup:

$c->res->header( content_disposition =3D>
        qq[attachment; filename=3D"$ascii_file"; filename*=3DUTF-8''$utf8_f=

The filename* is easy, I'm finding:

my $utf8_file =3D uri_escape( Encode::encode( 'UTF-8' =3D> $filename ) );

But the $ascii_file is a bit more work.  Percent-encoding doesn't work.
So, have to do a bit of filtering.

See any easier/cleaner/more-correct approach?   When I see this much code I
tend to think it's the wrong approach.

# Convert to ASCII using underscore as replacement

my $ascii_file =3D Encode::encode( ascii =3D> $filename, sub { '_' } );

# Remove quotes as we want to use quoted form of "filename" and preserve

$ascii_file =3D~ s/"/_/g;

# Replace non-printable characters with underscore, and collapse dups

$ascii_file =3D~ s/[^[:print:]]/_/g;
$ascii_file =3D~ s/_{2,}/_/g;

# Split off the extension so can check length of filename w/o extension.

# Of course, $ext could end up as dot + underscore.

my ( $base, $ext ) =3D split /(\.\w+)$/, $ascii_file;

# Use default filename if we don't have more than three "meaningful"

# very subjective.

$base =3D 'your_file' unless ( () =3D $base =3D~ /[A-Za-z0-9]/g ) > 3;

# Stuff the extension back on.

$ascii_file =3D $base;
$ascii_file .=3D $ext if defined $ext;

Again, "filename*" support is good, and I'm not trying to prevent buggy
clients from doing something stupid (e.g. filename=3D/etc/passwd), but want
to provide a reasonable fallback to "filename".

Perhaps the simple solution is to always use "filename=3Dyour_file" and hope
most clients use the filename* extension.

-- =

Bill Moseley
moseley at hank.org
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20131203/8ea44=

More information about the Catalyst mailing list