The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

HTTP::Upload::FlowJs::Examples - examples for HTTP::Upload::FlowJs

Pure PSGI example

This is a verbose example that assumes a PSGI / Plack environment and some helper routines to handle the payload of POST parameters.

ASSUMPTIONS

It is assumed in the below code that your HTTP framework implements some routines to retrieve information about the current request:

  sub params() {
      # return request parameters, both GET and POST parameters
      # as a hash reference
  }
  
  sub upload {
      # return something like a file system object to retrieve the content
      # of a file upload parameter
  }

SETUP

  use HTTP::Upload::FlowJs;

  my @parameter_names = (
    'file',                 # The name of the file
    'flowChunkNumber',      # The index of the chunk in the current upload.
                            # First chunk is 1 (no base-0 counting here).
    'flowTotalChunks',      # The total number of chunks.
    'flowCurrentChunkSize', # Current chunk size
    'flowChunkSize',        # The general chunk size. Using this value and
                            # flowTotalSize you can calculate the total number of
                            # chunks. Please note that the size of the data received in
                            # the HTTP might be lower than flowChunkSize of this for
                            # the last chunk for a file.
    'flowTotalSize',        #  The total file size.
    'flowIdentifier',       # A unique identifier for the file contained in the request.
    'flowFilename',         # The original file name (since a bug in Firefox results in
                            # the file name not being transmitted in chunk
                            # multipart posts).
    'flowRelativePath',     # The file's relative path when selecting a directory
                            # (defaults to file name in all browsers except Chrome).
  );
  
  my $uploads = '/tmp/flowjs_uploads/';
  my $flowjs = HTTP::Upload::FlowJs->new(
      incomingDirectory => $uploads,
      allowedContentType => sub { $_[0] =~ m!^image/! },
      parameter_names => \@parameter_names, # to override the default names
  );
  

POST handler

Your handler for POST requests is where you have to validate and store a received chunk. This is also where you determine whether a file was received completely and hand it off for further processing.

  sub POST_upload {
    my $params = params();

    my %info;
    @info{ @parameter_names } = @{$params}{@parameter_names};
    $info{ localChunkSize } = -s $params{ file };
    # or however you get the size of the uploaded chunk

    # you might want to set this so users don't clobber each others upload
    my $session_id = '';
    my @invalid = $flowjs->validateRequest( 'POST', \%info, $session_id );
    if( @invalid ) {
        warn 'Invalid flow.js upload request:';
        warn $_ for @invalid;
        return [500,[],["Invalid request"]];
        return;
    };

    if( $flowjs->disallowedContentType( \%info, $session_id )) {
        # We can determine the content type, and it's not an image
        return [415,[],["File type disallowed"]];
    };

    my $chunkname = $flowjs->chunkName( \%info, undef );

    # Save or copy the uploaded file
    upload('file')->copy_to($chunkname);

    # Now check if we have received all chunks of the file
    if( $flowjs->uploadComplete( \%info, undef )) {
        # Combine all chunks to final name

        my $digest = Digest::SHA256->new();

        my( $content_type, $ext ) = $flowjs->sniffContentType();
        my $final_name = "file1.$ext";
        open( my $fh, '>', $final_name )
            or die $!;
        binmode $fh;

        my( $ok, @unlink_chunks )
            = $flowjs->combineChunks( \%info, undef, $fh, $digest );
        unlink @unlink_chunks;

        # Notify backend that a file arrived
        print sprintf "File '%s' upload complete\n", $final_name;
    };

    # Signal OK
    return [200,[],[]]
  };

GET handler

The GET handler is used by the Javascript code to retrieve information about whether a chunk already has been uploaded or not.

  sub GET_upload {
    my $params = params();
    my %info;
    @info{ @parameter_names} = @{$params}{@parameter_names};
    my $flowjs = HTTP::Upload::FlowJs->new(
        incomingDirectory => $uploads,
        allowedContentType => sub { $_[0] =~ m!^image/! },
    );

    my @invalid = $flowjs->validateRequest( 'GET', \%info, session->{connid} );
    if( @invalid ) {
        warn 'Invalid flow.js upload request:';
        warn $_ for @invalid;
        return [500, [], [] ];

    } elsif( $flowjs->disallowedContentType( \%info, $session_id)) {
        # We can determine the content type, and it's not an image
        return [415,[],["File type disallowed"]];

    } else {
        my( $status, @messages )
            = $flowjs->chunkOK( $uploads, \%info, $session_id );
        if( $status != 500 ) {
            # 200 or 416
            return [$status, [], [] ];
        } else {
            warn $_ for @messages;
            return [$status, [], [] ];
        };
    };
  };