Backblaze-B2V4

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN


        # if you would rather download with the 84-character GUID
        my $response = $b2->b2_download_file_by_id(
          file_id => 'X-Bz-File-Id GUID from above',
          save_to_location => '/opt/majestica/tmp'
        );
        
        # For all of these $response is the output from the B2 V4 API
        
        # to confirm all is well
        my $all_is_well = $b2->current_status_is_not_ok != 1;
        # or 
        my $all_is_well = $b2->current_status_is_ok());
        
        # to get the latest error
        print $b2->latest_error();
        # if none, will be 'No error message found'

# DESCRIPTION / SET UP

This module should help you create buckets and store/retrieve files in the

lib/Backblaze/B2V4.pm  view on Meta::CPAN

use HTTP::Tiny;
use MIME::Base64;
use Path::Tiny;
use URI::Escape;

use Types::Common -all;
use Marlin::Util -all;
use Marlin
	'application_key_id' => { is => 'ro', isa => 'NonEmptyStr' },
	'application_key' => { is => 'ro', isa => 'NonEmptyStr' },
	'current_status_is_not_ok' => { is => 'rw', isa => 'Bool', default => 0 },
	'login_error' => { is => 'rw', isa => 'Bool', default => 0 },
	'errors' => { is => 'rw', isa => 'ArrayRef', default => [] },
	'api_info' => { is => 'rw', isa => 'HashRef', builder => 'build_api_info' },
	'bucket_info' => { is => 'rw', isa => 'HashRef', default => {} },
	'file_info' => { is => 'rw', isa => 'HashRef', default => {} },
	;

# builder method to create api_info hashref attribute
sub build_api_info ($self) {
	my $response = $self->send_request(

lib/Backblaze/B2V4.pm  view on Meta::CPAN

			$error_message = 'Error: ' . $response->{reason};
		}

		# track the error / set current state
		return $self->error_tracker(
			'error_message' => $error_message . ' (' . $response->{status} . ')',
			'url' => $args->url,
		);
	}	
	
	$self->current_status_is_not_ok(0);
	return $b2_response;
}

# indicate if everything is fine
sub current_status_is_ok ($self) {
	return !$self->current_status_is_not_ok;
}

# for tracking errors into $self->{errrors}[];
signature_for error_tracker => (
	method => true,
	named => [
		error_message => Str,
		url => Str,
	],
	returns => Bool,
);

sub error_tracker ($self, $args) {
	push(@{ $self->errors }, {
		'error_message' => $args->error_message,
		'url' => $args->url,
	});

	$self->current_status_is_not_ok(1);
	
	return 0;
}

# please tell me the lastest error message
sub latest_error ($self) {
	my $latest_error = $self->errors->[-1];
	if (!$latest_error || !$latest_error->{error_message}) {
		return 'No error message found';
	}

lib/Backblaze/B2V4.pm  view on Meta::CPAN

		save_to_location => Str, { optional => true },
	],
	returns => Bool|HashRef,
);

sub b2_download_file_by_id ($self, $args) {
	my $response = $self->send_request(
		'url' => $self->api_info->{download_url} . '/b2api/v4/b2_download_file_by_id' . '?fileId=' . $args->file_id,
	);

	if ($self->current_status_is_not_ok) {
		return 0;
	}

	# if they provided a save-to location (a directory) and the file was found, let's save it out
	if ($args->save_to_location) {
		$self->save_downloaded_file(
			save_to_location => $args->save_to_location, 
			response => $response
		);
	}

lib/Backblaze/B2V4.pm  view on Meta::CPAN

);

sub b2_download_file_by_name ($self, $args) {
	# send the request, as a GET
	my $response = $self->send_request(
		'url' => $self->api_info->{download_url} . '/file/' . uri_escape($args->bucket_name) . '/' . uri_escape($args->file_name),
	);

	# if the file was found, you will have the relevant headers in $response
	# as well as the file's contents in $response->{file_contents}
	if ($self->current_status_is_not_ok) {
		return 0;
	}

	# if they provided a save-to location (a directory) and the file was found, let's save it out
	if ($args->save_to_location) {
		$self->save_downloaded_file(
			save_to_location => $args->save_to_location, 
			response => $response
		);	
	}

lib/Backblaze/B2V4.pm  view on Meta::CPAN

		'url' => $upload_info->{upload_url},
		'authorization' => $upload_info->{authorization_token},
		'file_contents' => $file_contents,
		'headers' => {
			'X-Bz-File-Name' => uri_escape( $new_file_name ),
			'X-Bz-Content-Sha1' => sha1_hex( $file_contents ),
			'Content-Type' => $content_type,
		},
	);
	
	return $self->current_status_is_not_ok ? 0 : $response->{fileId};
}

# method to get the information needed to upload into a specific B2 bucket
signature_for b2_get_upload_info => (
	method => true,
	named => [
		bucket_name => NonEmptyStr,
	],
	returns => HashRef|Bool,
);

lib/Backblaze/B2V4.pm  view on Meta::CPAN

	if ($args->bucket_name) {
		$post_params->{bucketName} = $args->bucket_name;
	}

	# send the request
	my $response = $self->send_request(
		'url' => 'b2_list_buckets',
		'post_params' => $post_params,
	);

	if ($self->current_status_is_not_ok) {
		return 0;
	}

	# if we succeeded, load in all the found buckets to $self->{buckets}
	# that will be a hash of info, keyed by name

	my $bucket_name;
	foreach my $bucket_info (@{ $response->{buckets} }) {
		$bucket_name = $bucket_info->{bucketName};
		$self->bucket_info->{$bucket_name} = {

lib/Backblaze/B2V4.pm  view on Meta::CPAN

			'bucket_type' => $bucket_info->{bucketType},
		};
	}

	# if that bucket was not found, maybe they want to go ahead and create it?
	$bucket_name = $args->bucket_name;
	if ($bucket_name && !$self->bucket_info->{$bucket_name}->{bucket_id} && $args->auto_create_bucket) {
		$self->b2_bucket_maker(
			bucket_name => $bucket_name
		);
		if ($self->current_status_is_not_ok) {
			return 0;
		}
		# this will call back to me and get the info
	}
	
	return 1;
}

# method to retrieve file names / info from a bucket
# this client library is bucket-name-centric, so it looks for the bucket name as a arg

lib/Backblaze/B2V4.pm  view on Meta::CPAN

	if ($args->start_file_name) {
		$post_params->{startFileName} = $args->start_file_name;
	}

	my $response = $self->send_request(
		'url' => 'b2_list_file_names',
		'post_params' => $post_params,
	);

	# if we succeeded, read in the files
	if ($self->current_status_is_not_ok) {
		return 0;
	}
	
	$self->bucket_info->{$bucket_name}->{next_file_name} = $response->{nextFileName};

	# add to our possibly-started array of file info for this bucket
	push(
		@{ $self->{buckets}{$bucket_name}{files} },
		@{ $response->{files} }
	);

lib/Backblaze/B2V4.pm  view on Meta::CPAN

	
	if ($self->file_info->{$file_id} && ref($self->file_info->{$file_id}) eq 'HASH') {
		return $self->file_info->{$file_id};
	}

	# retrieve the file information
	my $response = $self->send_request(
		'url' => 'b2_get_file_info?fileId=' . $file_id,
	);
	
	if ($self->current_status_is_not_ok) {
		return 0;
	}

	# i am not going to waste the CPU cycles de-camelizing these sub-keys
	$self->file_info->{$file_id} = $response;

	return $response;
}

# method to create a bucket

lib/Backblaze/B2V4.pm  view on Meta::CPAN

			'algorithm' => 'AES256',
		};
	}

	# create the bucket...
	my $response = $self->send_request(
		'url' => 'b2_create_bucket',
		'post_params' => $post_params,
	);

	if ($self->current_status_is_not_ok) {
		return 0;
	}

	# otherwise successful, stash our new bucket into $self->{buckets}
	$self->bucket_info->{$args->bucket_name} = {
		'bucket_id' => $response->{bucketId},
		'bucket_type' => 'allPrivate',
	};

	return 1;

lib/Backblaze/B2V4.pm  view on Meta::CPAN


	# send the request
	$self->send_request(
		'url' => 'b2_delete_bucket',
		'post_params' => {
			'accountId' => $self->api_info->account_id,
			'bucketId' => $bucket_id,
		},
	);
	
	return $self->current_status_is_not_ok ? 0 : 1;
}

# method to delete a stored file object.  B2 thinks of these as 'versions,'
# but if you use unique names, one version = one file
signature_for b2_delete_file_version => (
	method => true,
	named => [
		file_name => NonEmptyStr,
		file_id => NonEmptyStr,
	],

lib/Backblaze/B2V4.pm  view on Meta::CPAN

sub b2_delete_file_version ($self, $args) {
	# send the request
	$self->send_request(
		'url' => 'b2_delete_file_version',
		'post_params' => {
			'fileName' => $args->file_name,
			'fileId' => $args->file_id,
		},
	);

	return $self->current_status_is_not_ok ? 0 : 1;
}

# method to upload a large file (>100MB)
signature_for b2_upload_large_file => (
	method => true,
	named => [
		new_file_name => NonEmptyStr,
		bucket_name => NonEmptyStr,
		file_location => NonEmptyStr,
		content_type => NonEmptyStr, { optional => true, default => 'b2/x-auto' },

lib/Backblaze/B2V4.pm  view on Meta::CPAN

	# and tell B2
	$response = $self->send_request(
		'url' => 'b2_finish_large_file',
		'post_params' => {
			'fileId' => $large_file_id,
			'partSha1Array' => \@sha1_array,
		},
	);

	# phew, i'm tired...
	return $self->current_status_is_not_ok ? 0 : $response->{fileId};
}

1;

__END__

=head1 NAME

Backblaze::B2V4 - Client library for the Backblaze B2 Cloud Storage Service V4 API.

lib/Backblaze/B2V4.pm  view on Meta::CPAN


	# if you would rather download with the 84-character GUID
	my $response = $b2->b2_download_file_by_id(
	  file_id => 'X-Bz-File-Id GUID from above',
	  save_to_location => '/opt/majestica/tmp'
	);
	
	# For all of these $response is the output from the B2 V4 API
	
	# to confirm all is well
	my $all_is_well = $b2->current_status_is_not_ok != 1;
	# or 
	my $all_is_well = $b2->current_status_is_ok());
	
	# to get the latest error
	print $b2->latest_error();
	# if none, will be 'No error message found'

=head1 DESCRIPTION / SET UP

This module should help you create buckets and store/retrieve files in the

lib/Backblaze/B2V4.pm  view on Meta::CPAN


See: https://www.backblaze.com/b2/docs/b2_get_upload_info.html

=head2 send_request

send_request() handles all the communications with B2.
You should be able to use this to make calls not explicitly
provided by this library.

If send_request() gets a 200 HTTP status from B2, then the call went
great, $b2->current_status_is_not_ok will be 0, and 
the JSON response will be returned.

If a 200 is not received from B2, $b2->current_status_is_not_ok
will be 1, and you can find an error in $b2->latest_error()

Note that the base URL for this API session will be stored
under $b2->api_info->{api_url} so that you build a URL like so:

$list_buckets_url = $b2->api_info->{api_url}.'/b2api/v4/b2_list_buckets';

Example of a GET API request:

	my $response = $b2->send_request(

script/b2_client  view on Meta::CPAN

	if ($b2->login_error) {
		die "ERROR: The B2 API keys you provided did not authenticate.  Please verify and try again.\n";
	}

	return $b2;
}

sub show_result {
	my ($b2, $result_message) = @_;

	if ($b2->current_status_is_not_ok) {
		return get_error_message($b2); 
	}

	return "\n" . $result_message . "\n";
}

sub get_error_message {
	my ($b2) = @_;

	if ($b2->latest_error() !~ /^Error/) {



( run in 1.846 second using v1.01-cache-2.11-cpan-7fcb06a456a )