You can use the amt-parameter in the read-function, documented here: https://botocore.amazonaws.com/v1/documentation/api/latest/reference/response.html.
And then use MultiPartUpload documented here, to upload the file piece by piece:
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#multipartupload
https://docs.aws.amazon.com/AmazonS3/latest/dev/mpuoverview.html
You should have a rule that deletes incomplete multipart uploads:
https://aws.amazon.com/es/blogs/aws/s3-lifecycle-management-update-support-for-multipart-uploads-and-delete-markers/
or else you may end up paying for incomplete data-parts stored in S3.
I copy-pasted something from my own script to to do this. This shows how you can stream all the way from downloading and to uploading. In case you have memory-limitations to consider. You could also alter this to store the file locally before you upload.
You will have to use MultiPartUpload anyway, since S3 have limitations on how large files you can upload in one action: https://aws.amazon.com/s3/faqs/
"The largest object that can be uploaded in a single PUT is 5 gigabytes. For objects larger than 100 megabytes, customers should consider using the Multipart Upload capability."
This is a code sample (I havent tested this code as it is here):
import boto3
amt = 1024*1024*10 # 10 MB at the time
session = boto3.Session(profile_name='yourprofile')
s3res = session.resource('s3')
source_s3file = "yourfile.file"
target_s3file = "yourfile.file"
source_s3obj = s3res.Object("your-bucket", source_s3file)
target_s3obj = s3res.Object("your-bucket", target_s3file)
# initiate MultiPartUpload
mpu = target_s3obj.initiate_multipart_upload()
partNr = 0
parts = []
body = source_s3obj.get()["Body"]
# get initial chunk
chunk = body.read(amt=amt).decode("utf-8") # this is where you use the amt-parameter
# Every time you call the read-function it reads the next chunk of data until its empty.
# Then do something with the chunk and upload it to S3 using MultiPartUpload
partNr += 1
part = mpu.Part(partNr)
response = part.upload(Body=chunk)
parts.append({
"PartNumber": partNr,
"ETag": response["ETag"]
})
while len(chunk) > 0:
# there is more data, get a new chunk
chunk = body.read(amt=amt).decode("utf-8")
# do something with the chunk, and upload the part
partNr += 1
part = mpu.Part(partNr)
response = part.upload(Body=chunk)
parts.append({
"PartNumber": partNr,
"ETag": response["ETag"]
})
# no more chunks, complete the upload
part_info = {}
part_info["Parts"] = parts
mpu_result = mpu.complete(MultipartUpload=part_info)