Skip to content

Instantly share code, notes, and snippets.

@metadaddy
Last active May 24, 2022
Embed
What would you like to do?
PHP sample code for a multipart file upload to Backblaze B2. Usage: php php_multipart.php [-v] <bucket_name> <local_file>
<?php
// PHP sample code to show multipart file upload using the B2 Native API
// User supplies optional -v for verbose output from curl, then bucket name
// and path to local file
$rest_index = null;
$opts = getopt("v::", [], $rest_index);
$args = array_slice($argv, $rest_index);
$verbose = (count($opts) == 1);
if (count($args) != 2) {
echo "Usage: $argv[0] [-v] <bucket_name> <local_file>\n";
exit(1);
}
$bucket_name = $args[0];
$local_file = $args[1];
// Safest way to pass credentials is as environment variables
$application_key_id = getenv("B2_APPLICATION_KEY_ID");
$application_key = getenv("B2_APPLICATION_KEY");
if (!$application_key_id || !$application_key) {
echo "You must set the B2_APPLICATION_KEY_ID and B2_APPLICATION_KEY environment variables";
exit(2);
}
//
// First, call b2_authorize_account to get authorization token
//
// Basic auth for token request
$credentials = base64_encode("$application_key_id:$application_key");
$url = "https://api.backblazeb2.com/b2api/v2/b2_authorize_account";
$session = curl_init($url);
$headers = array(
"Accept: application/json",
"Authorization: Basic $credentials"
);
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
curl_setopt($session, CURLOPT_HTTPGET, true);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
curl_setopt($session, CURLOPT_VERBOSE, $verbose);
echo "Calling b2_authorize_account... ";
$server_output = curl_exec($session);
$http_code = curl_getinfo($session, CURLINFO_HTTP_CODE);
curl_close ($session);
echo "status: $http_code \n";
echo "$server_output\n";
$auth_response = json_decode($server_output);
$account_id = $auth_response->accountId;
$api_url = $auth_response->apiUrl;
$auth_token = $auth_response->authorizationToken;
//
// Get the id from the bucket name
//
$session = curl_init($api_url . "/b2api/v2/b2_list_buckets");
$data = array(
"accountId" => $account_id,
"bucketName" => $bucket_name
);
$headers = array(
"Authorization: $auth_token"
);
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
curl_setopt($session, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($session, CURLOPT_POST, true);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
curl_setopt($session, CURLOPT_VERBOSE, $verbose);
echo "Calling b2_list_buckets... ";
$server_output = curl_exec($session);
$http_code = curl_getinfo($session, CURLINFO_HTTP_CODE);
curl_close ($session);
echo "status: $http_code \n";
echo "$server_output\n";
$list_response = json_decode($server_output);
$bucket_id = $list_response->buckets[0]->bucketId;
//
// Set up the large file upload
//
$file_name = basename($local_file);
$content_type = "b2/x-auto";
$data = array(
"fileName" => $file_name,
"bucketId" => $bucket_id,
"contentType" => $content_type
);
$headers = array(
"Accept: application/json",
"Authorization: $auth_token"
);
$session = curl_init("$api_url/b2api/v2/b2_start_large_file");
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
curl_setopt($session, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
curl_setopt($session, CURLOPT_VERBOSE, $verbose);
echo "Calling b2_start_large_file... ";
$server_output = curl_exec($session);
$http_code = curl_getinfo($session, CURLINFO_HTTP_CODE);
curl_close ($session);
echo "status: $http_code \n";
echo "$server_output\n";
$start_response = json_decode($server_output);
$file_id = $start_response->fileId;
// This sample uses the smallest permitted part size, 5 MB, to more easily show
// multipart uploads in action. The default part size is 100 MB
$part_size = 5 * 1000 * 1000;
$local_file_size = filesize($local_file);
$total_bytes_sent = 0;
$sha1_of_parts = array();
$part_no = 1;
$file_handle = fopen($local_file, "r");
// Loop through parts of file
//
// Note - for simplicity, this sample does not handle errors or implement the
// retry logic outlined at https://www.backblaze.com/b2/docs/uploading.html
//
// Production code should implement error handling and retry requests.
//
while ($total_bytes_sent < $local_file_size) {
//
// Get upload part URL
//
$data = array("fileId" => $file_id);
$headers = array(
"Accept: application/json",
"Authorization: $auth_token"
);
$session = curl_init("$api_url/b2api/v2/b2_get_upload_part_url");
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
curl_setopt($session, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
curl_setopt($session, CURLOPT_VERBOSE, $verbose);
echo "b2_get_upload_part_url...";
$server_output = curl_exec($session);
$http_code = curl_getinfo($session, CURLINFO_HTTP_CODE);
curl_close ($session);
echo "status: $http_code \n";
echo "$server_output\n";
$part_response = json_decode($server_output);
$large_file_auth_token = $part_response->authorizationToken;
$upload_url = $part_response->uploadUrl;
$bytes_sent_for_part = min($local_file_size - $total_bytes_sent, $part_size);
//
// Upload the part to the URL we just received
//
// Get a sha1 of the part we are going to send
fseek($file_handle, $total_bytes_sent);
$data_part = fread($file_handle, $bytes_sent_for_part);
array_push($sha1_of_parts, sha1($data_part));
fseek($file_handle, $total_bytes_sent);
$session = curl_init($upload_url);
// NOTE - we need to set a blank Transfer-Encoding header, otherwise PHP will
// default to chunked transfer encoding and remove the content length header
$headers = array(
"Accept: application/json",
"Authorization: $large_file_auth_token",
"Transfer-Encoding:",
"Content-Length: $bytes_sent_for_part",
"X-Bz-Part-Number: $part_no",
"X-Bz-Content-Sha1: {$sha1_of_parts[$part_no - 1]}"
);
curl_setopt($session, CURLOPT_POST, true);
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
curl_setopt($session, CURLOPT_INFILE, $file_handle);
curl_setopt($session, CURLOPT_INFILESIZE, (int)$bytes_sent_for_part);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
curl_setopt($session, CURLOPT_VERBOSE, $verbose);
echo "Calling b2_upload_part with $bytes_sent_for_part bytes... ";
$server_output = curl_exec($session);
$http_code = curl_getinfo($session, CURLINFO_HTTP_CODE);
curl_close ($session);
echo "status: $http_code\n";
echo "$server_output\n";
$part_no++;
$total_bytes_sent += $bytes_sent_for_part;
}
fclose($file_handle);
//
// Finish the upload
//
$session = curl_init("$api_url/b2api/v2/b2_finish_large_file");
$data = array(
"fileId" => $file_id,
"partSha1Array" => $sha1_of_parts
);
$headers = array(
"Accept: application/json",
"Authorization: $auth_token"
);
curl_setopt($session, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
curl_setopt($session, CURLOPT_VERBOSE, $verbose);
echo "b2_finish_large_file... ";
$server_output = curl_exec($session);
$http_code = curl_getinfo($session, CURLINFO_HTTP_CODE);
curl_close ($session);
echo "status: $http_code\n";
echo "$server_output\n";
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment