Amazon 360-images require a specific naming convention for their 360-images. Every item as a global identifier called an ASIN (Amazon Standard Identification Number) and the ASIN must be in 3 places.

My final code looks something like this:

asin = item.asins.first
zipfile = Tempfile.new([asin, '.zip'])
image_files = []
Zip::File.open(zipfile.path, Zip::File::CREATE) do |zipfile|
  item.images_blobs.order("active_storage_blobs.filename ASC").each_with_index do |blob, index|
    dir_filename = "#{asin}_360/#{asin}_360_#{index.to_s.rjust(4, '0')}_web.#{image_file_format}"

    variant_options = {
      gravity: "Center",
      background: "white",
      resize: "#{image_size}x#{image_size}",
      extent: "#{image_size}x#{image_size}",
      format: image_file_format
    }
    # Create a new tempfile for storing the downloaded blob (Not yet handled the situation if these images are  too large)
    image_file = URI.open(blob.variant(variant_options).processed.service_url)

    # maintain a reference to the tempfile so it doesn't get garbage collected
    image_files.push(image_file)

    # add it to the zipfile
    zipfile.add(dir_filename, image_file.path)
  end
end

# Attach Zip file to the parent
item.three_sixty_exports.attach(io: File.open(zipfile.path, 'rb'), filename: "#{asin}.zip", content_type: 'application/zip')

Some items have multiple ASINs, so we actually iterate through this process for each ASIN.