I am trying to force download a file by sending http headers via perl. the current code is as follow:
#!/usr/bin/perl
use strict;
use Session;
use CGI::Carp qw(fatalsToBrowser);
use HTTP::Headers;
HTTP::Headers->new(
Content_type => 'text/plain',
Content_disposition => 'attachment; filename=export.txt',
);
print 'just some text';
exit;
I have also included HTTP::Headers however when I run this, it prints out the text instead of downloading the content...
You're just constructing the HTTP::Headers, but it's never printed to stdout. So you have to call also the as_string method:
my $h = HTTP::Headers->new(
Content_type => 'text/plain',
Content_disposition => 'attachment; filename=export.txt',
);
print $h->as_string;
But this is just printing the HTTP header without the separator between header and body. If you want to let libwww-perl to do this for you, you can also use HTTP::Message:
use HTTP::Headers;
use HTTP::Message;
my $h = HTTP::Headers->new(
Content_type => 'text/plain',
Content_disposition => 'attachment; filename=export.txt',
);
my $content = 'just some text';
my $msg = HTTP::Message->new($h, $content);
print $msg->as_string;
To be more correct, you should probably use "\r\n" as line terminators:
print $msg->as_string("\015\012");
Another alternative is to use CGI.pm, and use the header method, which can be used to set response HTTP headers. Actually, using CGI.pm is more common than using the HTTP::* classes. The latter are more common in use when dealing with dealing with LWP::UserAgent to fetch web pages.
When you are using CGI, you can set it when printing the header via CGI that way:
my $q = CGI->new();
print $q->header(
-type => 'text/plain',
-charset => 'iso-8859-1',
-attachment => 'filename.txt',
);
ok I figured out a simpler way...
instead of using HTTP::Headers I simply printed out the following lines:
print"Content-type:text/plain\n";
print"Content-disposition:attachment; filename=export.txt\n\n";
which did the trick...
This is the code using the CGI module:
use CGI qw(:standard);
print header(-type => "text/plain", -content_disposition => "attachment; filename=filename=export.txt");
print "just some text";
Related
I am writing a Perl script to POST an attachment to JIRA using
REST::Client to access the API
but I am getting an error.
use REST::Client;
use warnings;
use strict;
use File::Slurp;
use MIME::Base64;
my $user = 'user';
my $pass = 'pass';
my $url = "http://******/rest/api/2/issue/BugID/attachments";
my $client = REST::Client->new();
$client->addHeader( 'Authorization', 'Basic' . encode_base64( $user . ':' . $pass ) );
$client->addHeader( 'X-Atlassian-Token', 'no-check' );
$client->setHost( $url );
# my %header = ('Authorization' => 'Basic'. encode_base64($user . ':' . $pass),'X-Atlassian-Token' => 'no-check');
my $attachment = "C:\\Folder\\Test.txt";
$client->POST(
$url,
'Content_Type' => 'form-data',
'Content' => [ 'file' => [$attachment] ]
);
if ( $client->responseCode() eq '200' ) {
print "Updated\n";
}
# print the result
print $client->responseContent() . "\n";
The error I get is
REST::Client exception: headers must be presented as a hashref at C:\Users\a\filename.pl line 24.
As shown in the code, I have tried setting headers in different ways but I still get same error.
Please suggest if there is any other method.
I have tried using JIRA module but it gives error too.
According to the documentation, the POST method:
Takes an optional body content and hashref of custom request headers.
You need to put your headers in a hashref, e.g.:
$client->POST($url, $content, {
foo => 'bar',
baz => 'qux'
});
But...it looks like you're expecting REST::Client to use HTTP::Request::Common to construct a multipart/form-data request. Unfortunately, that's not the case, so you'll have to build the content by hand.
You could use HTTP::Request::Common directly like this:
use strict;
use warnings 'all';
use 5.010;
use HTTP::Request::Common;
use REST::Client;
my $client = REST::Client->new;
my $url = 'http://www.example.com';
my $req = POST($url,
Content_Type => 'form-data',
Content => [ file => [ 'foo.txt' ] ]
);
$client->POST($url, $req->content(), {
$req->headers->flatten()
});
But this is a bit convoluted; I would recommend dropping REST::Client and using LWP::UserAgent instead. REST::Client is just a thin wrapper for LWP::UserAgent with a few convenience features, like prepending a default host to all requests. In this case, it's just getting in the way and I don't think the conveniences are worth the trouble.
From the documentation:
POST ( $url, [$body_content, %$headers] )
And you're doing:
$client->POST(
$url,
'Content_Type' => 'form-data',
'Content' => [ 'file' => [$attachment] ]
);
So - passing a list of scalars, with an arrayref at the end.
Perhaps you want something like:
$client->POST(
$url,
$attachment,
{ 'Content-Type' => 'form-data' }
);
Note the {} to construct an anonymous hash for the headers.
Although you probably want to open and include the 'attachment', because there's nothing in REST::Client about opening files and sending them automagically.
I believe I have a general Perl problem, rather than an LWP::UserAgent problem... however its somewhat complex.
The task is to write a test-script that does a SWORD deposit.
I create tests by first writing code to prove the thing works, then add in the Test::More wrappers to make it a test.
BACKGROUND
A SWORD deposit is simply an http post request with a bunch of defined headers, and the content of the body being the thing to be ingested. This all works fine, I can perform the actions through CURL, and I've written scripts to do this.... but within a a larger application environment (that'll be EPrints.)
CODE
My problem, I believe, comes when I try to attach the contents of the file on the disk.
#!/home/cpan/bin/perl
use strict;
use warnings;
use LWP::UserAgent;
##use WWW::Mechanize;
use File::Slurp;
use MIME::Base64;
my $auth = 'username:password';
my $domain = 'devel.example.com';
my $ua = LWP::UserAgent->new();
my $basedir = "./test_files";
my $package = 'http://opendepot.org/europePMC/2.0';
my $filename = "$basedir/PMC165035.zip";
my $mime = 'application/zip';
print "filename: $filename\n";
my $deposit_url = $domain . '/sword-app/deposit/archive';
my $file = read_file( $filename, { binmode => ':raw' } );
# Set up the SWORD deposit
my $autho = "Basic " . MIME::Base64::encode( $auth, '' );
my %headers = (
'X-Packaging' => $package,
'X-No-Op' => 'false',
'X-Verbose' => 'true',
'Content-Disposition' => "filename=$filename",
'Content-Type' => $mime,
'User-Agent' => 'Broker Test Harness',
'Authorization' => $autho,
);
my $r = $ua->post( $deposit_url, %headers, Content => $file );
# DEBUG TEST
write_file('foo.zip', $file);
my $ret = $r->decoded_content;
print "Content: $ret\n";
if ( $r->is_success ) { print "Deposited $package successfully" }
WHAT WORKS, WHAT DOESN'T
This code is lifted pretty much directly from working code I have - the only difference is that the working code gets the content for $file via an object-call within EPrints.
I know the file exists on the disk, if I do an ls -l on the filename printed, I can see the file, and its readable
In the code above, there is a line write_file('foo.zip', $file); - that writes a file which unzip -l foo.zip happily tells me has 3 files in it.
The line print "Content: $ret\n"; should print an atom response - for me, it prints nothing....
The Access log reports an error 500, but there's diddly-squat in the error-log.
The help
What I need to know is how I get the actual contents of the .zip file into the content part of the LWP::UserAgent post request...
(I'm going to spend much time not trying to dig into EPrints, to track where the error-500 is coming from, and why nothing appears in the log file.... but that's probably going to be down to an issue with what's been posted)
The solution lies in realizing what LWP POST is doing.
my $filename = "$basedir/PMC165035.zip";
my $file = read_file( $filename, { binmode => ':raw' } );
my %headers = (
'X-Packaging' => $package,
'X-No-Op' => 'false',
'X-Verbose' => 'true',
'Content-Disposition' => "filename=$filename",
'Content-Type' => $mime,
'User-Agent' => 'Broker Test Harness',
'Authorization' => $autho,
);
All work by setting $filename to be something like /home/services/foo/testing/test_files/PMC165035.zip, and passing this (full) filename to the server example.com.
The problem is that the server is looking for a filename, not a filename-with-path... so when the service does its thing with the file by dumping the content into its temporary upload location, and then it looks for ~~~temp_location/home/services/foo/testing/test_files/PMC165035.zip, it can't find it!
The solution is to read in the file, but ensure that the filename given in the headers is just the filename, not with-a-path
I have the following code and want to attach a file using an API. This code is delivering me the URL but the file is not getting attached.
#!/usr/bin/perl
use LWP::UserAgent;
$ua = LWP::UserAgent->new;
my $response = $ua->post(Content_Type => 'application/xml');
#$ua->agent("Mozilla 8.0 blah...");
use HTTP::Request::Common qw(POST);
use LWP::UserAgent(POST);
my $request=(POST "http://Server/Test.jsp",
Content =>[
external => "false",
Filedata => "C:/Location.jpg"
]);
#$request = $ua->request($request);
my $results=$ua->request($request);
$content = $request->content;
print $content;
exit;
Well, first you have to specify the correct content-type.
my $request=(POST "http://Server/watson/api/bug/addAttachmentAPI.jsp",
Content_Type => 'form-data',
Content =>[
appGUID => "Test GUID",
Second, the file specification must be an array reference of the form [ $file, $name, ... ] where ... are optional header field/value pairs (if you don't include headers, the content-type of the file will be guessed).
Filedata => ["C:Test Location/Upload/APIs.jpg", 'APIs.jpg'],
]);
See HTTP::Request::Common for more information.
I need to create an HTTP::Response with compressed data. How do I go about making the content gzipped? Do I just add the appropriate headers and compress it myself using Compress::Zlib? Or does any of the LWP modules provide a method for handling this?
Is this what you need? You gzip the data, set the Content-encoding header, and send it off.
use strict;
use warnings;
use HTTP::Response;
use IO::Compress::Gzip qw(gzip);
my $data = q(My cat's name is Buster);
my $gzipped_data;
my $gzip = gzip \$data => \$gzipped_data;
print STDERR $gzipped_data;
my $response = HTTP::Response->new;
$response->code( 200 );
$response->header( 'Content-type' => 'text/plain' );
$response->header( 'Content-encoding' => 'gzip' );
$response->header( 'Content-length' => length $gzipped_data );
$response->content( $gzipped_data );
print $response->as_string;
I have a perl CGI application that I want to take the users request headers, and turn those around into an LWP::UserAgent get request. Basically the goal is to replicate the incoming users headers and use those to make a separate request.
I've tried to create the headers myself but when I attempt to display the CGI headers and then my clone UserAgent headers, they aren't exactly the same. Here's what I got:
my $cgi = new CGI;
my %headers = map { $_ => $cgi->http($_) } $cgi->http;
my $req_headers = HTTP::Headers->new( %headers );
my $ua = LWP::UserAgent->new( default_headers => $req_headers );
print Dumper $ua->default_headers;
Basically, %headers and $ua->default_headers are not identical. $ua->default_headers has an agent that identifies itself as a perl script. I can manually set $ua->agent("") but there are other imperfections and the headers still aren't identical.
What's the best way to do what I want? There's got to be an easier solution...
It looks like the problem has to do with naming of incoming http headers compared to what HTTP::Headers uses.
The incoming parameters all have HTTP_ prefix in them where HTTP::Headers doesn't use that naming convention (which makes sense). Plus it looks like (a quick read in the code) that HTTP::Headers does the right thing in converting '-' into '_' for its own use.
I would recommended changing your map to following that removes the prefix:
# remove leading HTTP_ from keys, note: this assumes all keys have pattern
# HTTP_*
%headers = map { ( /^HTTP_(.*?)$/ ) => $cgi->http($_) } $cgi->http;
Here is the debugging script I used:
my $cgi = CGI->new;
my %headers = map { $_ => $cgi->http($_) } $cgi->http;
my $req_headers = HTTP::Headers->new( %headers );
my $ua = LWP::UserAgent->new( default_headers => $req_headers );
print "Content-type: text/plain\n\n";
print Dumper($ua->default_headers);
print Dumper( \%headers );
# remove HTTP_ from $_
%headers = map { ( /^HTTP_(.*?)$/ ) => $cgi->http($_) } $cgi->http;
$req_headers = HTTP::Headers->new( %headers );
$ua = LWP::UserAgent->new( default_headers => $req_headers );
print "headers part deux:\n";
print Dumper( $ua );
Hope that Helps