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! attachment = post_file! render json: attachment, status: :ok else render status: :ok end rescue render status: :internal_server_error 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, :_chunkNumber, :_totalChunks, :_chunkFilename, :_chunkIdentifier, :_chunkSize, :_currentChunkSize, :_totalSize) 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! return nil unless chunks_params[:file].try(:tempfile).is_a? Tempfile # Ensure required paths exist FileUtils.mkpath(chunk_file_directory) # Move the temporary file upload to the temporary chunk file path FileUtils.mv(chunks_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? chunks_params[:_chunkFilename].split('.').last end ## # Determine if this is the last chunk based in parts count. def last_chunk? Dir["#{chunk_file_directory}/#{chunks_params[:_chunkFilename]}.part*"].count == chunks_params[:_totalChunks].to_i end ## # ./tmp/file-chunks/abc-123/upload.txt.part1 def chunk_file_path File.join(chunk_file_directory, "#{chunks_params[:_chunkFilename]}.part#{chunks_params[:_chunkNumber]}") end ## # ./tmp/file-chunks/abc-123 def chunk_file_directory File.join('tmp', 'file-chunks', chunks_params[:_chunkIdentifier]) end ## # /tmp/file-chunks/upload.txt def final_file_path File.join(final_file_directory, chunks_params[:_chunkFilename]) end ## # /tmp/file-chunks def final_file_directory File.join('tmp', 'files', chunks_params[:_chunkIdentifier]) 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