b2_upload_part
Uploads one part of a large file to B2, using an file ID obtained
from b2_start_large_file
.
Once all of the parts are uploaded, use
b2_finish_large_file
to make the file available for use.
If you upload the same part number for the same file more than once, both uploads will succeed, and the second upload will overwrite the first.
Request
The upload request is a POST. The file name and other parameters are in the request headers, and the file to be uploaded is the request body.
URL Path
Use the b2_get_upload_part_url
operation to get a URL you can use to
upload files. The URL it returns will contain your bucket ID and the
upload destination, and will look something like this:
https://pod-000-1016-09.backblaze.com/b2api/v2/b2_upload_part/4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001/0037
Request HTTP Headers
Authorization
required
An upload authorization token, from b2_get_upload_part_url
.
The token must have the writeFiles
capability.
X-Bz-Part-Number
required
A number from 1 to 10000. The parts uploaded for one file must have contiguous numbers, starting with 1.
Content-Length
required
The number of bytes in the file being uploaded. Note that this
header is required; you cannot leave it out and just use chunked
encoding.
The minimum size of every part but the last one is 5MB.
When sending the SHA1 checksum at the end, the Content-Length should
be set to the size of the file plus the 40 bytes of hex checksum.
X-Bz-Content-Sha1
required
The SHA1 checksum of this part of the file. B2 will check this
when the part is uploaded, to make sure that the data arrived
correctly.
The same SHA1 checksum must be passed to
b2_finish_large_file
.
You may optionally provide the SHA1 at the end of the
upload. See the section on Uploading.
X-Bz-Server-Side-Encryption-Customer-Algorithm
optional
This header is required if
b2_start_large_file
was called with parameters
specifying Server-Side
Encryption with Customer-Managed Keys (SSE-C), in which case
its value must match the serverSideEncryption
algorithm
requested via
b2_start_large_file
.
X-Bz-Server-Side-Encryption-Customer-Key
optional
This header is required if
b2_start_large_file
was called with parameters
specifying Server-Side
Encryption with Customer-Managed Keys (SSE-C), in which case
its value must match the serverSideEncryption
customerKey
requested via
b2_start_large_file
.
X-Bz-Server-Side-Encryption-Customer-Key-Md5
optional
This header is required if
b2_start_large_file
was called with parameters
specifying Server-Side
Encryption with Customer-Managed Keys (SSE-C), in which case
its value must match the serverSideEncryption
customerKeyMd5
requested via
b2_start_large_file
.
Request HTTP Message Body Parameters
There are no JSON parameters allowed. The file to be uploaded is the message body and is not encoded in any way. It is not URL encoded. It is not MIME encoded.
Response
Response HTTP Status 200
File part successfully uploaded. The JSON response will contain:
fileId
The file ID for uploading this file.
partNumber
Which part this is.
contentLength
The number of bytes stored in the part.
contentSha1
The SHA1 of the bytes stored in the part.
contentMd5
optional
The MD5 of the bytes stored in the part. Not all parts have an MD5 checksum, so this field is optional, and set to null for parts that do not have one.
serverSideEncryption
optional
When the part is encrypted with Server-Side Encryption, the mode ("SSE-B2" or "SSE-C") and algorithm used to encrypt the data.
uploadTimestamp
This is a UTC time when this part was uploaded. It is a base 10 number of milliseconds since midnight, January 1, 1970 UTC. This fits in a 64 bit integer such as the type "long" in the programming language Java. It is intended to be compatible with Java's time long. For example, it can be passed directly into the java call Date.setTime(long time).
Response Errors
File not uploaded.
If possible the server will return a JSON error structure. Errors include:
status |
code |
description |
---|---|---|
400 |
auth_token_limit |
The auth token is already being used. For more information, see Uploading in Parallel. |
400 |
bad_bucket_id |
The requested bucket ID does not match an existing bucket. |
400 |
bad_request |
The request had the wrong fields or illegal values. The message returned with the error will describe the problem. |
400 |
cannot_delete_non_empty_bucket |
A bucket must be empty before it can be deleted. To delete this bucket, first remove all of the files in the bucket, then try the delete operation again. |
401 |
unauthorized |
The auth token used is valid, but does not authorize this call with these parameters. The capabilities of an auth token are determined by the application key used with b2_authorize_account. |
401 |
bad_auth_token |
The auth token used is not valid. Call b2_get_upload_part_url again to get a new one. |
401 |
expired_auth_token |
The auth token used has expired. Call b2_get_upload_part_url again to get a new one. |
405 |
method_not_allowed |
only POST is supported |
408 |
request_timeout |
The service timed out reading the uploaded file |
503 |
service_unavailable |
Call b2_get_upload_part_url again to get a new auth token. |
Some errors returned mean that you must call
b2_get_upload_url
again to get a new upload URL and authorization token. See
Uploading for details.
API Versions
v1: Add uploadTimestamp field (August 30, 2018)
The response now includes the same uploadTimestamp
field
returned by b2_list_buckets.
v1: Application keys (July 26, 2018)
Incompatible change: After calling b2_authorize_account with an application key that does not have the right permissions, this call will return a 401 Unauthorized.
v1: Original release (September 22, 2015)
Sample Code
Code
FILE_NAME='./bigfile.dat'
FILE_SIZE=`stat -s $FILE_NAME | awk '{print $'8'}' | sed 's/st_size=\([0-9]*\)/\1/g'`
# Prepare the large file be spliting it into chunks
split -b 100000000 $FILE_NAME bz_chunk_
FILE_CHUNK_NAMES=(`ls -1 bz_chunk_*`)
# Upload file
UPLOAD_URL=''; # Provided by b2_get_upload_part_url
UPLOAD_AUTHORIZATION_TOKEN=''; # Provided by b2_get_upload_part_url
let MINIMUM_PART_SIZE=(100 * 1000 * 1000); # Provided by b2_authorize_account. Default 100 MB
TOTAL_BYTES_SENT=0;
BYTES_SENT=$MINIMUM_PART_SIZE;
PART_NO=1;
PART_SHA1_ARRAY=();
while [ $TOTAL_BYTES_SENT -lt $FILE_SIZE ]; do
if [ $(($FILE_SIZE-$TOTAL_BYTES_SENT)) -lt $MINIMUM_PART_SIZE ]; then
let BYTES_SENT=$(($FILE_SIZE - $TOTAL_BYTES_SENT));
fi
let IDX=PART_NO-1;
CHUNK_FILE=${FILE_CHUNK_NAMES[IDX]};
CHUNK_SHA1=`openssl sha1 $CHUNK_FILE | awk '{print $'2'}'`;
PART_SHA1_ARRAY=(${PART_SHA1_ARRAY[@]} $CHUNK_SHA1);
curl \
-H "Authorization: $UPLOAD_AUTHORIZATION_TOKEN" \
-H "X-Bz-Part-Number: $PART_NO" \
-H "X-Bz-Content-Sha1: ${PART_SHA1_ARRAY[$IDX]}" \
-H "Content-Length: $BYTES_SENT" \
--data-binary "@$CHUNK_FILE" \
$UPLOAD_URL
let TOTAL_BYTES_SENT+=$BYTES_SENT;
let PART_NO+=1;
done
Output
Assuming ~208 MB File
Part 1:
{
"contentLength": 100000000,
"contentSha1": "062685a84ab248d2488f02f6b01b948de2514ad8",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 1
}
Part 2:
{
"contentLength": 100000000,
"contentSha1": "cf634751c3d9f6a15344f23cbf13f3fc9542addf",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 2
}
Part 3:
{
"contentLength": 8158542,
"contentSha1": "4546018a346df683acc9a3367977de4cc8024965",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 3
}
Code
import java.io.*;
import java.util.*;
import javax.json.*;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
String uploadUrl = ""; // Provided by b2_get_upload_part_url
String uploadAuthorizationToken = ""; // Provided by b2_get_upload_part_url
long minimumPartSize = 0; // Provided by b2_authorize_account
// Open the file and
File localFile = new File("bigfile.dat");
long localFileSize = localFile.length();
long totalBytesSent = 0;
long bytesSentForPart = minimumPartSize;
byte[] buf = new byte[(int)minimumPartSize];
ArrayList<String> partSha1Array = new ArrayList<String>();
int partNo = 1;
while (totalBytesSent < localFileSize) {
// Determine how many bytes we are going to send
if ((localFileSize - totalBytesSent) < minimumPartSize) {
bytesSentForPart = (localFileSize - totalBytesSent);
}
try {
// Get the the SHA1 of the one parts we are uploading
FileInputStream fileInputStream = new FileInputStream(localFile);
fileInputStream.skip(totalBytesSent);
int numBytesReadIntoBuff = fileInputStream.read(buf, 0, (int) bytesSentForPart);
fileInputStream.close();
MessageDigest sha1Digest = MessageDigest.getInstance("SHA-1");
sha1Digest.update(buf, 0, (int) bytesSentForPart);
byte[] sha1Buf = sha1Digest.digest();
String hexSha1 = String.format("%040x", new BigInteger(1, sha1Buf));
partSha1Array.add(hexSha1);
// Send it over the wire
URL url = new URL(uploadUrl);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Authorization", uploadAuthorizationToken);
connection.setRequestProperty("X-Bz-Part-Number", new Integer(partNo).toString());
connection.setRequestProperty("Content-Length", new Long(bytesSentForPart).toString());
connection.setRequestProperty("X-Bz-Content-Sha1", partSha1Array.get(partNo - 1));
connection.setDoOutput(true);
DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
dataOutputStream.write(buf, 0, (int) bytesSentForPart);
dataOutputStream.close();
String jsonResponse = myInputStreamReader(connection.getInputStream());
System.out.println(jsonResponse);
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
} catch (IOException iox) {
iox.printStackTrace();
} catch (NoSuchAlgorithmException nsae) {
nsae.printStackTrace();
} finally {
connection.disconnect();
}
// Get read for the next iteration of the loop
totalBytesSent = totalBytesSent + bytesSentForPart;
partNo++;
}
// Input stream reader example.
static public String myInputStreamReader(InputStream in) throws IOException {
InputStreamReader reader = new InputStreamReader(in);
StringBuilder sb = new StringBuilder();
int c = reader.read();
while (c != -1) {
sb.append((char)c);
c = reader.read();
}
reader.close();
return sb.toString();
}
Output
Assuming ~208 MB File
Part 1:
{
"contentLength": 100000000,
"contentSha1": "062685a84ab248d2488f02f6b01b948de2514ad8",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 1
}
Part 2:
{
"contentLength": 100000000,
"contentSha1": "cf634751c3d9f6a15344f23cbf13f3fc9542addf",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 2
}
Part 3:
{
"contentLength": 8158542,
"contentSha1": "4546018a346df683acc9a3367977de4cc8024965",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 3
}
Code
import base64
import json
import urllib2
import os
import hashlib
upload_authorization_token = "" # Provided by b2_get_upload_part_url
local_file = "/path/to/file/on/disk"
local_file_size = os.stat(local_file).st_size
size_of_part = minimum_part_size
total_bytes_sent = 0
part_no = 1
part_sha1_array = []
while (total_bytes_sent < local_file_size):
if ((local_file_size - total_bytes_sent) < minimum_part_size):
size_of_part = local_file_size - total_bytes_sent
filed = open(local_file)
filed.seek(total_bytes_sent)
file_data = filed.read(size_of_part)
filed.close()
sha1_digester = hashlib.new('SHA1')
sha1_digester.update(file_data)
sha1_str = sha1_digester.hexdigest()
part_sha1_array.append(sha1_str)
request = urllib2.Request(
upload_url,
file_data,
headers = { 'Authorization': upload_authorization_token,
'X-Bz-Part-Number': part_no,
'Content-Length': size_of_part,
'X-Bz-Content-Sha1': sha1_str }
)
response = urllib2.urlopen(request)
response_data = json.loads(response.read())
response.close()
print response_data
total_bytes_sent = total_bytes_sent + size_of_part
part_no += 1
Output
Assuming ~208 MB File
Part 1:
{
"contentLength": 100000000,
"contentSha1": "062685a84ab248d2488f02f6b01b948de2514ad8",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 1
}
Part 2:
{
"contentLength": 100000000,
"contentSha1": "cf634751c3d9f6a15344f23cbf13f3fc9542addf",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 2
}
Part 3:
{
"contentLength": 8158542,
"contentSha1": "4546018a346df683acc9a3367977de4cc8024965",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 3
}
Code
import Foundation
// For this example we are assuming a file size of 200 MB consisting of
// the letter 'B'. In the example, we will upload two 100 MB parts.
// The sample code will acquire two upload URLs which are required when
// uploading file parts simultaneously (see the Large Files documentation
// for more detail).
//
// Other assumptions:
//
// SHA1 - We assume you have a way to produce a SHA1 from the given
// data objects. If you are using the Apple platform please take a
// look at importing <CommonCrypto/CommonCrypto.h>. If you are
// using Linux OpenSSL or <blah> can also generate a SHA1.
//
// Hex String - We assume that you have a routine to convert a Data
// object to a Hex string. This is will be used to create a
// Hex string representation of the upload parts.
//
// Generate the parts of the large 'B' file. Typically you would read
// the file off of disk.
// Call b2_authorize_account you need this for an authorization token
let authInfo = "" // Obtained by b2_authorize_account
let authToken = "" // Obtained by b2_authorize_account
// Call b2_start_large_file you need this for the file ID which is
// required by b2_get_upload_part_url.
// Since we are simultaneously uploading both parts we need an upload
// authorization token and upload url for each part. See
// b2_get_upload_part_url documentation for more information.
//
// Call b2_get_upload_part_url to get a URL and auth token for the 1st part
let dataPart1 = Data(repeating: UInt8(ascii: "B"), count: (100 * 1024 * 1024))
let dataPart1ShaHexStr = ""
// Prepare the upload request for part 1
let uploadUrlForPart1 = URL(string: "<upload part url obtained from b2_get_upload_part_url>")!
let uploadAuthTokenForPart1 = "<upload part authorization token obtained from b2_get_upload_part_url>"
var requestForPart1 = URLRequest(url: uploadUrlForPart1)
requestForPart1.httpMethod = "POST"
requestForPart1.addValue(uploadAuthTokenForPart1, forHTTPHeaderField: "Authorization")
requestForPart1.addValue(String(1), forHTTPHeaderField: "X-Bz-Part-Number")
requestForPart1.addValue(String(dataPart1.count), forHTTPHeaderField: "Content-Length")
requestForPart1.addValue(dataPart1ShaHexStr, forHTTPHeaderField: "X-Bz-Content-Sha1")
// Create the task
let taskForPart1 = URLSession.shared.uploadTask(with: requestForPart1, from: dataPart1) { (data, response, error) in
if let jsonData = data {
let json = String(data: jsonData, encoding: .utf8)
print("\(json!)")
}
}
// Start the task
taskForPart1.resume()
// Call b2_get_upload_part_url to get a URL and auth token for the 2nd part
let dataPart2 = Data(repeating: UInt8(ascii: "B"), count: (100 * 1024 * 1024))
let dataPart2ShaHexStr = ""
// Upload Part 2
let uploadUrlForPart2 = URL(string: "<upload part url obtained from b2_get_upload_part_url>")!
let uploadAuthTokenForPart2 = "<upload part authorization token obtained from b2_get_upload_part_url>"
var request = URLRequest(url: uploadUrlForPart2)
request.httpMethod = "POST"
request.addValue(uploadAuthTokenForPart2, forHTTPHeaderField: "Authorization")
request.addValue(String(2), forHTTPHeaderField: "X-Bz-Part-Number")
request.addValue(String(dataPart2.count), forHTTPHeaderField: "Content-Length")
request.addValue(dataPart2ShaHexStr, forHTTPHeaderField: "X-Bz-Content-Sha1")
let taskForPart2 = URLSession.shared.uploadTask(with: request, from: dataPart2) { (data, response, error) in
if let jsonData = data {
let json = String(data: jsonData, encoding: .utf8)
print("\(json!)")
}
}
// Start the task
taskForPart2.resume()
// Swift 4.1 (swiftlang-902.0.48 clang-902.0.37.1) Xcode 9.3.1 (9E501)
Output
Assuming ~200 MB File
Part 1:
{
"contentLength": 100000000,
"contentSha1": "062685a84ab248d2488f02f6b01b948de2514ad8",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 1
}
Part 2:
{
"contentLength": 100000000,
"contentSha1": "cf634751c3d9f6a15344f23cbf13f3fc9542addf",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 2
}
Code
require 'json'
require 'net/http'
require 'digest/sha1'
minimum_part_size_bytes = 100 * (1000 * 1000) # Obtained from b2_authorize_account. The default is 100 MB
upload_url = "" # Provided by b2_get_upload_url
local_file = "<Path to File to Upload>" # File to be uploaded
upload_authorization_token = "" # Provided by b2_get_upload_url
content_type = "" # The content type of the file
local_file_path = "" # Path to the file you want to upload
local_file_size = File.stat(local_file_path).size
total_bytes_sent = 0
bytes_sent_for_part = minimum_part_size_bytes
sha1_of_parts = Array.new # SHA1 of each uploaded part. You will need to save these because you will need them in b2_finish_large_file
part_no = 1
while total_bytes_sent < local_file_size do
# Determine num bytes to send
if ((local_file_size - total_bytes_sent) < minimum_part_size_bytes)
bytes_sent_for_part = (local_file_size - total_bytes_sent)
end
# Read file into memory and calculate a SHA1
file_part_data = File.read(local_file_path, bytes_sent_for_part, total_bytes_sent, mode: "rb")
sha1_of_parts.push(Digest::SHA1.hexdigest(file_part_data))
hex_digest_of_part = sha1_of_parts[part_no - 1]
# Send it over the wire
uri = URI(upload_url)
req = Net::HTTP::Post.new(uri)
req.add_field("Authorization","#{upload_authorization_token}")
req.add_field("X-Bz-Part-Number","#{part_no}")
req.add_field("X-Bz-Content-Sha1","#{hex_digest_of_part}")
req.add_field("Content-Length", "#{bytes_sent_for_part}")
req.body = file_part_data
http = Net::HTTP.new(req.uri.host, req.uri.port)
http.use_ssl = (req.uri.scheme == 'https')
res = http.start {|http| http.request(req)}
case res
when Net::HTTPSuccess then
json = res.body
puts json
when Net::HTTPRedirection then
fetch(res['location'], limit - 1)
else
res.error!
end
# Prepare for the next iteration of the loop
total_bytes_sent += bytes_sent_for_part
#offset = total_bytes_sent
part_no += 1
end
Output
Assuming ~208 MB File
Part 1:
{
"contentLength": 100000000,
"contentSha1": "062685a84ab248d2488f02f6b01b948de2514ad8",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 1
}
Part 2:
{
"contentLength": 100000000,
"contentSha1": "cf634751c3d9f6a15344f23cbf13f3fc9542addf",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 2
}
Part 3:
{
"contentLength": 8158542,
"contentSha1": "4546018a346df683acc9a3367977de4cc8024965",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 3
}
Code
using System;
using System.Net;
using System.Text;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
FileInfo fileInfo = new FileInfo("<Path to file>");
long localFileSize = fileInfo.Length;
long totalBytesSent = 0;
long bytesSentForPart = 100 * (1000 * 1000);
long minimumPartSize = bytesSentForPart;
ArrayList partSha1Array = new ArrayList();
byte[] data = new byte[100 * (1000 * 1000)];
int partNo = 1;
while (totalBytesSent < localFileSize)
{
if ((localFileSize - totalBytesSent) < minimumPartSize)
{
bytesSentForPart = (localFileSize - totalBytesSent);
}
// Generate SHA1
FileStream f = File.OpenRead("<Path to file>");
f.Seek(totalBytesSent, SeekOrigin.Begin);
f.Read(data, 0, (int)bytesSentForPart);
SHA1 sha1 = SHA1.Create();
byte[] hashData = sha1.ComputeHash(data, 0, (int)bytesSentForPart);
StringBuilder sb = new StringBuilder();
foreach (byte b in hashData)
{
sb.Append(b.ToString("x2"));
}
f.Close();
partSha1Array.Add(sb.ToString());
HttpWebRequest uploadPartRequest = (HttpWebRequest)WebRequest.Create(getUploadUrlData.uploadUrl);
uploadPartRequest.Method = "POST";
uploadPartRequest.Headers.Add("Authorization", getUploadUrlData.authorizationToken);
uploadPartRequest.Headers.Add("X-Bz-Part-Number", partNo.ToString());
uploadPartRequest.Headers.Add("X-Bz-Content-Sha1", (String)partSha1Array[(partNo - 1)]);
uploadPartRequest.ContentType = "application/json; charset=utf-8";
uploadPartRequest.ContentLength = bytesSentForPart;
using (Stream stream = uploadPartRequest.GetRequestStream())
{
stream.Write(data, 0, (int)bytesSentForPart);
stream.Close();
}
HttpWebResponse uploadPartResponse = null;
try
{
uploadPartResponse = (HttpWebResponse)uploadPartRequest.GetResponse();
}
catch (WebException e)
{
using (HttpWebResponse errorResponse = (HttpWebResponse)e.Response)
{
Console.WriteLine("Error code: {0}", errorResponse.StatusCode);
using (StreamReader reader = new StreamReader(errorResponse.GetResponseStream()))
{
String text = reader.ReadToEnd();
Console.WriteLine(text);
}
}
}
finally
{
uploadPartResponse.Close();
}
partNo++;
totalBytesSent = totalBytesSent + bytesSentForPart;
}
Output
under construction
Code
<?php
// Used by curl when CURLOPT_READFUNCTION is set
function myReadFile($curl_rsrc, $file_pointer, $length) {
return fread($file_pointer, $length);
}
// Upload parts
$minimum_part_size = 100 * (1000 * 1000); // Obtained from b2_authorize_account. The default is 100 MB
$local_file = "<path to large file>";
$local_file_size = filesize($local_file);
$total_bytes_sent = 0;
$bytes_sent_for_part = 0;
$bytes_sent_for_part = $minimum_part_size;
$sha1_of_parts = Array();
$part_no = 1;
$file_handle = fopen($local_file, "r");
while($total_bytes_sent < $local_file_size) {
// Determine the number of bytes to send based on the minimum part size
if (($local_file_size - $total_bytes_sent) < $minimum_part_size) {
$bytes_sent_for_part = ($local_file_size - $total_bytes_sent);
}
// 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);
// Send it over th wire
$session = curl_init($upload_url);
// Add headers
$headers = array();
$headers[] = "Accept: application/json";
$headers[] = "Authorization: " . $large_file_auth_token;
$headers[] = "Content-Length: " . $bytes_sent_for_part;
$headers[] = "X-Bz-Part-Number: " . $part_no;
$headers[] = "X-Bz-Content-Sha1: " . $sha1_of_parts[$part_no - 1];
curl_setopt($session, CURLOPT_POST, true);
curl_setopt($session, CURLOPT_HTTPHEADER, $headers); // Add headers
curl_setopt($session, CURLOPT_INFILE, $file_handle);
curl_setopt($session, CURLOPT_INFILESIZE, (int)$bytes_sent_for_part);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Receive server response
curl_setopt($session, CURLOPT_READFUNCTION, "myReadFile");
$server_output = curl_exec($session);
curl_close ($session);
print $server_output . "\n";
// Prepare for the next iteration of the loop
$part_no++;
$total_bytes_sent = $bytes_sent_for_part + $total_bytes_sent;
$read_file_bytes_read = 0;
}
fclose($file_handle);
?>
Output
Assuming ~208 MB File
Part 1:
{
"contentLength": 100000000,
"contentSha1": "062685a84ab248d2488f02f6b01b948de2514ad8",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 1
}
Part 2:
{
"contentLength": 100000000,
"contentSha1": "cf634751c3d9f6a15344f23cbf13f3fc9542addf",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 2
}
Part 3:
{
"contentLength": 8158542,
"contentSha1": "4546018a346df683acc9a3367977de4cc8024965",
"fileId": "4_ze73ede9c9c8412db49f60715_f100b4e93fbae6252_d20150824_m224353_c900_v8881000_t0001",
"partNumber": 3
}