class V1::LearningObjects::ChunksController < ApplicationController before_action :authorize! # GET /learning_objects/:learning_object_id/chunk def show # chunk exist? render status: File.exist?(chunk_file_path) ? 200 : 204 end # POST /learning_objects/:learning_object_id/chunk def create save_file! if last_chunk? combine_file! post_file! end render status: 200 rescue render status: 500 end private def authorize! @learning_object = LearningObject.find chunks_params[:id] authorize(@learning_object, :update?) end # Never trust parameters from the scary internet, only allow the white list through. def chunks_params params.permit(:id, :file, :flowChunkNumber, :flowTotalChunks, :flowFilename, :flowIdentifier) end def post_file! publisher = LearningObjectPublisher.new(DspaceService.create_client) publisher.upload @learning_object, final_file_path end ## # Move the temporary Sinatra upload to the chunk file location def save_file! # Ensure required paths exist FileUtils.mkpath(chunk_file_directory) # Move the temporary file upload to the temporary chunk file path FileUtils.mv(params[:file][:tempfile], chunk_file_path, force: true) end ## # Build final file def combine_file! # Ensure required paths exist FileUtils.mkpath(final_file_directory) # Open final file in append mode File.open(final_file_path, 'a') do |f| file_chunks.each do |file_chunk_path| # Write each chunk to the permanent file f.write File.read(file_chunk_path) end end # Cleanup chunk file directory and all chunk files FileUtils.rm_rf(chunk_file_directory) end def valid_mime_type? mime_types = @learning_object.object_type.mime_types.map(&:extension) return true if mime_types.empty? mime_types.include? params[:flowFilename].split('.').last end ## # Determine if this is the last chunk based in parts count. def last_chunk? Dir["#{chunk_file_directory}/#{params[:flowFilename]}.part*"].count == params[:flowTotalChunks].to_i end ## # ./tmp/flow/abc-123/upload.txt.part1 def chunk_file_path File.join(chunk_file_directory, "#{params[:flowFilename]}.part#{params[:flowChunkNumber]}") end ## # ./tmp/flow/abc-123 def chunk_file_directory File.join('tmp', 'flow', params[:flowIdentifier]) end ## # /tmp/flow/upload.txt def final_file_path File.join(final_file_directory, params[:flowFilename]) end ## # /tmp/flow def final_file_directory File.join('tmp', 'flow') end ## # Get all file chunks sorted by cardinality of their part number def file_chunks Dir["#{chunk_file_directory}/*.part*"].sort_by { |f| f.split('.part')[1].to_i } end end