Skip to content

Instantly share code, notes, and snippets.

@rwunsch
Created November 10, 2023 08:31
Show Gist options
  • Save rwunsch/f72692a9a1bb99d2906918e518a6324d to your computer and use it in GitHub Desktop.
Save rwunsch/f72692a9a1bb99d2906918e518a6324d to your computer and use it in GitHub Desktop.
AEMaaCS - AEM as a Cloud Services - File and Folder upload shell script ( initiateUpload , uploadToken , fileName , mimeType )
#!/bin/bash
# Check if pv is installed
if ! command -v pv &> /dev/null; then
echo "Error: 'pv' command not found. Please install it before running the script."
exit 1
fi
# Check if jq is installed
if ! command -v jq &> /dev/null; then
echo "Error: 'jq' command not found. Please install it before running the script."
exit 1
fi
# Set DEBUG to true to enable debug statements
DEBUG=true
# Function for printing debug statements
function debug() {
if [ "${DEBUG}" = true ]; then
echo "[DEBUG] $1"
fi
}
# Function to check if a file exists
function file_exists() {
[ -e "$1" ]
}
# Function to check if a path is a directory
function is_directory() {
[ -d "$1" ]
}
# Check if the required number of parameters are provided
if [ "$#" -ne 4 ]; then
echo "Usage: $0 <aem-url> <asset-folder> <file-to-upload> <bearer-token>"
exit 1
fi
AEM_URL="$1"
ASSET_FOLDER="$2"
FILE_TO_UPLOAD="$3"
BEARER_TOKEN="$4"
# Extracting file name or folder name from the file path
NAME=$(basename "${FILE_TO_UPLOAD}")
# Step 1: Check if "file-to-upload" is a folder
if is_directory "${FILE_TO_UPLOAD}"; then
echo "Uploading files from the folder recursively..."
# Recursively upload files in the folder
find "${FILE_TO_UPLOAD}" -type f | while read -r FILE_PATH; do
FILE_NAME=$(basename "${FILE_PATH}")
debug "Uploading file: ${FILE_PATH}"
# You can choose to initiate upload for each file here
# For simplicity, let's assume you use the same ASSET_FOLDER for all files
./aem-upload.sh "${AEM_URL}" "${ASSET_FOLDER}" "${FILE_PATH}" "${BEARER_TOKEN}"
done
else
# "file-to-upload" is a single file
FILE_NAME="${NAME}"
# Step 2: Calculate File Size
FILE_SIZE=$(stat -c %s "${FILE_TO_UPLOAD}")
# Step 3: Initiate Upload
INITIATE_UPLOAD_ENDPOINT="${AEM_URL}/content/dam/${ASSET_FOLDER}.initiateUpload.json"
debug "Initiating upload..."
debug "Initiate Upload Endpoint: ${INITIATE_UPLOAD_ENDPOINT}"
debug "File Name: ${FILE_NAME}"
debug "File Size: ${FILE_SIZE}"
INITIATE_UPLOAD_RESPONSE=$(curl -X POST \
-H "Authorization: Bearer ${BEARER_TOKEN}" \
-H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
-d "fileName=${FILE_NAME}" \
-d "fileSize=${FILE_SIZE}" \
${INITIATE_UPLOAD_ENDPOINT})
# Continue with the rest of the script...
fi
# Check if the response body contains the specified HTML content for a 404 error
if echo "${INITIATE_UPLOAD_RESPONSE}" | grep -q "<title>404 Specified folder not found</title>"; then
echo "Folder not found. Creating the folder..."
# Attempt to create the folder
CREATE_FOLDER_ENDPOINT="${AEM_URL}/api/assets/${ASSET_FOLDER}"
debug "Creating folder..."
debug "Create Folder Endpoint: ${CREATE_FOLDER_ENDPOINT}"
CREATE_FOLDER_RESPONSE=$(curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${BEARER_TOKEN}" \
-d '{"class":"'${ASSET_FOLDER}'","properties":{"title":"'${ASSET_FOLDER}'"}}' \
${CREATE_FOLDER_ENDPOINT})
# Check the response code and inform the user accordingly
STATUS_CODE_CREATE_FOLDER=$(echo "${CREATE_FOLDER_RESPONSE}" | jq -r '.properties."status.code"')
case ${STATUS_CODE_CREATE_FOLDER} in
201)
echo "Folder created successfully. Initiating upload again..."
# Retry Initiate Upload after creating the folder
INITIATE_UPLOAD_RESPONSE=$(curl -X POST \
-H "Authorization: Bearer ${BEARER_TOKEN}" \
-H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
-d "fileName=${FILE_NAME}" \
-d "fileSize=${FILE_SIZE}" \
${INITIATE_UPLOAD_ENDPOINT})
;;
409)
echo "Error: Folder already exists."
;;
412)
echo "Error: Precondition failed. Root collection cannot be found or accessed."
exit 1
;;
500)
echo "Error: Internal Server Error. Something went wrong."
exit 1
;;
*)
echo "Error: Unexpected response code ${STATUS_CODE_CREATE_FOLDER}"
exit 1
;;
esac
fi
# Extracting values from the response
FOLDER_PATH=$(echo "${INITIATE_UPLOAD_RESPONSE}" | jq -r '.folderPath')
UPLOAD_URIS=($(echo "${INITIATE_UPLOAD_RESPONSE}" | jq -r '.files[0].uploadURIs[]'))
UPLOAD_TOKEN=$(echo "${INITIATE_UPLOAD_RESPONSE}" | jq -r '.files[0].uploadToken')
MIME_TYPE=$(echo "${INITIATE_UPLOAD_RESPONSE}" | jq -r '.files[0].mimeType')
MIN_PART_SIZE=$(echo "${INITIATE_UPLOAD_RESPONSE}" | jq -r '.files[0].minPartSize')
MAX_PART_SIZE=$(echo "${INITIATE_UPLOAD_RESPONSE}" | jq -r '.files[0].maxPartSize')
COMPLETE_URI=$(echo "${INITIATE_UPLOAD_RESPONSE}" | jq -r '.completeURI')
# Extracting "Affinity-cookie" from the response headers
AFFINITY_COOKIE=$(echo "${INITIATE_UPLOAD_RESPONSE}" | grep -i 'Affinity-cookie' | awk '{print $2}')
debug "Folder Path: ${FOLDER_PATH}"
debug "Upload URIs: ${UPLOAD_URIS[@]}"
debug "Upload Token: ${UPLOAD_TOKEN}"
debug "MIME Type: ${MIME_TYPE}"
debug "Min Part Size: ${MIN_PART_SIZE}"
debug "Max Part Size: ${MAX_PART_SIZE}"
debug "Complete URI: ${COMPLETE_URI}"
debug "Affinity Cookie: ${AFFINITY_COOKIE}"
# Calculate the number of parts needed
NUM_PARTS=$(( (FILE_SIZE + MAX_PART_SIZE - 1) / MAX_PART_SIZE ))
# Calculate the part size for the last chunk
LAST_PART_SIZE=$(( FILE_SIZE % MAX_PART_SIZE ))
if [ "${LAST_PART_SIZE}" -eq 0 ]; then
LAST_PART_SIZE=${MAX_PART_SIZE}
fi
# Step 4: Upload binary to the blob store in parts
PART_NUMBER=1
for UPLOAD_URI in "${UPLOAD_URIS[@]}"; do
PART_SIZE=${MAX_PART_SIZE}
if [ ${PART_NUMBER} -eq ${NUM_PARTS} ]; then
PART_SIZE=${LAST_PART_SIZE}
debug "Last part size: ${PART_SIZE}"
fi
PART_FILE="/tmp/${FILE_NAME}_part${PART_NUMBER}"
# Check if the part file already exists
if file_exists "${PART_FILE}"; then
debug "Using existing part file: ${PART_FILE}"
else
dd if="${FILE_TO_UPLOAD}" of="${PART_FILE}" bs=${PART_SIZE} count=1 2>/dev/null
debug "Creating part file: ${PART_FILE}"
fi
debug "Uploading part ${PART_NUMBER}..."
debug "Part File: ${PART_FILE}"
debug "Upload URI: ${UPLOAD_URI}"
# Upload the part in the background
if command -v pv &> /dev/null; then
pv "${PART_FILE}" | curl --progress-bar -X PUT --data-binary "@-" "${UPLOAD_URI}" &
else
curl -# -X PUT --data-binary "@${PART_FILE}" "${UPLOAD_URI}" &
fi
PART_NUMBER=$((PART_NUMBER + 1))
done
# Wait for all background processes to finish
wait
# Step 5: Complete the upload in AEM
COMPLETE_UPLOAD_ENDPOINT="${AEM_URL}${COMPLETE_URI}"
debug "Completing the upload..."
debug "Complete Upload Endpoint: ${COMPLETE_UPLOAD_ENDPOINT}"
curl -X POST \
-H "Authorization: Bearer ${BEARER_TOKEN}" \
-H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
-H "Affinity-cookie: ${AFFINITY_COOKIE}" \
--data-urlencode "uploadToken=${UPLOAD_TOKEN}" \
--data-urlencode "fileName=${FILE_NAME}" \
--data-urlencode "mimeType=${MIME_TYPE}" \
"${COMPLETE_UPLOAD_ENDPOINT}"
echo "File upload completed successfully."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment