Uploading files to S3 account from Linux command line
Solution 1:
S3cmd does what you want. Uploading and downloading files, syncing directories and creating buckets.
S3cmd is a free command line tool and client for uploading, retrieving and managing data in Amazon S3 and other cloud storage service providers that use the S3 protocol, such as Google Cloud Storage or DreamHost DreamObjects. It is best suited for power users who are familiar with command line programs. It is also ideal for batch scripts and automated backup to S3, triggered from cron, etc.
Solution 2:
Amazon provides their own CLI tools now too.
From http://aws.amazon.com/cli/
Using familiar syntax, you can view the contents of your S3 buckets in a directory-based listing.
$ aws s3 ls s3://mybucket LastWriteTime Length Name ------------- ------ ---- PRE myfolder/ 2013-09-03 10:00:00 1234 myfile.txt ...
You can perform recursive uploads and downloads of multiple files in a single folder-level command. The AWS CLI will run these transfers in parallel for increased performance.
$ aws s3 cp myfolder s3://mybucket/myfolder --recursive upload: myfolder/file1.txt to s3://mybucket/myfolder/file1.txt upload: myfolder/subfolder/file1.txt to s3://mybucket/myfolder/subfolder/file1.txt ...
A sync command makes it easy to synchronize the contents of a local folder with a copy in an S3 bucket.
$ aws s3 sync myfolder s3://mybucket/myfolder --exclude *.tmp upload: myfolder/newfile.txt to s3://mybucket/myfolder/newfile.txt ...
Documentation for file related commands is here.
Solution 3:
If you can't (perhaps you're on a shared host) or don't want to install extra tools, it is possible to just use bash, curl, and openssl.
http://tmont.com/blargh/2014/1/uploading-to-s3-in-bash
file=/path/to/file/to/upload.tar.gz
bucket=your-bucket
resource="/${bucket}/${file}"
contentType="application/x-compressed-tar"
dateValue=`date -R`
stringToSign="PUT\n\n${contentType}\n${dateValue}\n${resource}"
s3Key=xxxxxxxxxxxxxxxxxxxx
s3Secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
signature=`echo -en ${stringToSign} | openssl sha1 -hmac ${s3Secret} -binary | base64`
curl -L -X PUT -T "${file}" \
-H "Host: ${bucket}.s3.amazonaws.com" \
-H "Date: ${dateValue}" \
-H "Content-Type: ${contentType}" \
-H "Authorization: AWS ${s3Key}:${signature}" \
https://${bucket}.s3.amazonaws.com/${file}
Note that I modified this script from the one in the above link. I added the -L
option because AWS may insert a redirect in there. The -L
option will follow the redirect for you.
One other caveat. This won't work for files larger than 5GB. Those require a multi-part upload that would require a more complex script.
Solution 4:
A POSIX-compliant shell script that requires openssl, curl and sed only;
supporting AWS Signature Version 4, which is required for region
eu-central-1
(Frankfurt) and recommended for the others:
https://gist.github.com/vszakats/2917d28a951844ab80b1
#!/bin/sh -u
# To the extent possible under law, Viktor Szakats (vszakats.net)
# has waived all copyright and related or neighboring rights to this
# script.
# CC0 - https://creativecommons.org/publicdomain/zero/1.0/
# Upload a file to Amazon AWS S3 using Signature Version 4
#
# docs:
# https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
#
# requires:
# curl, openssl 1.x, GNU sed, LF EOLs in this file
fileLocal="${1:-example-local-file.ext}"
bucket="${2:-example-bucket}"
region="${3:-}"
storageClass="${4:-STANDARD}" # or 'REDUCED_REDUNDANCY'
m_openssl() {
if [ -f /usr/local/opt/[email protected]/bin/openssl ]; then
/usr/local/opt/[email protected]/bin/openssl "$@"
elif [ -f /usr/local/opt/openssl/bin/openssl ]; then
/usr/local/opt/openssl/bin/openssl "$@"
else
openssl "$@"
fi
}
m_sed() {
if which gsed > /dev/null 2>&1; then
gsed "$@"
else
sed "$@"
fi
}
awsStringSign4() {
kSecret="AWS4$1"
kDate=$(printf '%s' "$2" | m_openssl dgst -sha256 -hex -mac HMAC -macopt "key:${kSecret}" 2>/dev/null | m_sed 's/^.* //')
kRegion=$(printf '%s' "$3" | m_openssl dgst -sha256 -hex -mac HMAC -macopt "hexkey:${kDate}" 2>/dev/null | m_sed 's/^.* //')
kService=$(printf '%s' "$4" | m_openssl dgst -sha256 -hex -mac HMAC -macopt "hexkey:${kRegion}" 2>/dev/null | m_sed 's/^.* //')
kSigning=$(printf 'aws4_request' | m_openssl dgst -sha256 -hex -mac HMAC -macopt "hexkey:${kService}" 2>/dev/null | m_sed 's/^.* //')
signedString=$(printf '%s' "$5" | m_openssl dgst -sha256 -hex -mac HMAC -macopt "hexkey:${kSigning}" 2>/dev/null | m_sed 's/^.* //')
printf '%s' "${signedString}"
}
iniGet() {
# based on: https://stackoverflow.com/questions/22550265/read-certain-key-from-certain-section-of-ini-file-sed-awk#comment34321563_22550640
printf '%s' "$(m_sed -n -E "/\[$2\]/,/\[.*\]/{/$3/s/(.*)=[ \\t]*(.*)/\2/p}" "$1")"
}
# Initialize access keys
if [ -z "${AWS_CONFIG_FILE:-}" ]; then
if [ -z "${AWS_ACCESS_KEY:-}" ]; then
echo 'AWS_CONFIG_FILE or AWS_ACCESS_KEY/AWS_SECRET_KEY envvars not set.'
exit 1
else
awsAccess="${AWS_ACCESS_KEY}"
awsSecret="${AWS_SECRET_KEY}"
awsRegion='us-east-1'
fi
else
awsProfile='default'
# Read standard aws-cli configuration file
# pointed to by the envvar AWS_CONFIG_FILE
awsAccess="$(iniGet "${AWS_CONFIG_FILE}" "${awsProfile}" 'aws_access_key_id')"
awsSecret="$(iniGet "${AWS_CONFIG_FILE}" "${awsProfile}" 'aws_secret_access_key')"
awsRegion="$(iniGet "${AWS_CONFIG_FILE}" "${awsProfile}" 'region')"
fi
# Initialize defaults
fileRemote="${fileLocal}"
if [ -z "${region}" ]; then
region="${awsRegion}"
fi
echo "Uploading" "${fileLocal}" "->" "${bucket}" "${region}" "${storageClass}"
echo "| $(uname) | $(m_openssl version) | $(m_sed --version | head -1) |"
# Initialize helper variables
httpReq='PUT'
authType='AWS4-HMAC-SHA256'
service='s3'
baseUrl=".${service}.amazonaws.com"
dateValueS=$(date -u +'%Y%m%d')
dateValueL=$(date -u +'%Y%m%dT%H%M%SZ')
if hash file 2>/dev/null; then
contentType="$(file -b --mime-type "${fileLocal}")"
else
contentType='application/octet-stream'
fi
# 0. Hash the file to be uploaded
if [ -f "${fileLocal}" ]; then
payloadHash=$(m_openssl dgst -sha256 -hex < "${fileLocal}" 2>/dev/null | m_sed 's/^.* //')
else
echo "File not found: '${fileLocal}'"
exit 1
fi
# 1. Create canonical request
# NOTE: order significant in ${headerList} and ${canonicalRequest}
headerList='content-type;host;x-amz-content-sha256;x-amz-date;x-amz-server-side-encryption;x-amz-storage-class'
canonicalRequest="\
${httpReq}
/${fileRemote}
content-type:${contentType}
host:${bucket}${baseUrl}
x-amz-content-sha256:${payloadHash}
x-amz-date:${dateValueL}
x-amz-server-side-encryption:AES256
x-amz-storage-class:${storageClass}
${headerList}
${payloadHash}"
# Hash it
canonicalRequestHash=$(printf '%s' "${canonicalRequest}" | m_openssl dgst -sha256 -hex 2>/dev/null | m_sed 's/^.* //')
# 2. Create string to sign
stringToSign="\
${authType}
${dateValueL}
${dateValueS}/${region}/${service}/aws4_request
${canonicalRequestHash}"
# 3. Sign the string
signature=$(awsStringSign4 "${awsSecret}" "${dateValueS}" "${region}" "${service}" "${stringToSign}")
# Upload
curl -s -L --proto-redir =https -X "${httpReq}" -T "${fileLocal}" \
-H "Content-Type: ${contentType}" \
-H "Host: ${bucket}${baseUrl}" \
-H "X-Amz-Content-SHA256: ${payloadHash}" \
-H "X-Amz-Date: ${dateValueL}" \
-H "X-Amz-Server-Side-Encryption: AES256" \
-H "X-Amz-Storage-Class: ${storageClass}" \
-H "Authorization: ${authType} Credential=${awsAccess}/${dateValueS}/${region}/${service}/aws4_request, SignedHeaders=${headerList}, Signature=${signature}" \
"https://${bucket}${baseUrl}/${fileRemote}"
Notice, the script will enable server-side
AES256 encryption by default.
Solution 5:
Alternatively you can try https://github.com/minio/mc
mc
provides minimal tools to work with Amazon S3 compatible cloud storage and filesystems. It has features like resumable uploads, progress bar, parallel copy. mc
is written in Golang and released under Apache license v2.