Skip to content
Snippets Groups Projects
Commit 845a9c89 authored by Mateus Rambo Strey's avatar Mateus Rambo Strey
Browse files

support flowjs instead of resumablejs in chunks upload

parent f0bd3723
No related branches found
No related tags found
1 merge request!316support flowjs instead of resumablejs in chunks upload
class V1::LearningObjects::ChunksController < ApplicationController class V1::LearningObjects::ChunksController < ApplicationController
before_action :authorize! before_action :authorize!
before_action :chunk_service
# GET /learning_objects/:learning_object_id/chunk # GET /learning_objects/:learning_object_id/chunk
def show def show
if @chunk.exist? # chunk exist?
post_file render status: File.exist?(chunk_file_path) ? 200 : 204
render status: :ok
else
render status: :not_found # chunk doesnt exists and needs to be uploaded
end
end end
# POST /learning_objects/:learning_object_id/chunk # POST /learning_objects/:learning_object_id/chunk
def create def create
@chunk.save save_file!
post_file if last_chunk?
combine_file!
post_file!
end
render status: :ok render status: 200
rescue
render status: 500
end end
private private
def chunk_service def authorize!
@chunk = ChunksService.new(chunks_params) @learning_object = LearningObject.find chunks_params[:id]
return render status: :unsupported_media_type if @chunk.nil? authorize(@learning_object, :update?)
end end
def post_file # Never trust parameters from the scary internet, only allow the white list through.
return false unless @chunk.concatenated def chunks_params
params.permit(:id, :file, :flowChunkNumber, :flowTotalChunks, :flowFilename, :flowIdentifier)
end
def post_file!
publisher = LearningObjectPublisher.new(DspaceService.create_client) publisher = LearningObjectPublisher.new(DspaceService.create_client)
publisher.upload @chunk.learning_object, @chunk.resumable_filename publisher.upload @learning_object, final_file_path
end end
def authorize! ##
learning_object = LearningObject.find chunks_params[:id] # Move the temporary Sinatra upload to the chunk file location
authorize(learning_object, :update?) 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 end
# Never trust parameters from the scary internet, only allow the white list through. ##
def chunks_params # Build final file
params.permit(:id, :file, :resumableIdentifier, :resumableFilename, :resumableChunkNumber, :resumableTotalChunks, :resumableChunkSize, :resumableCurrentChunkSize, :resumableTotalSize, :resumableType, :resumableRelativePath) 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
end end
class ChunksService
attr_reader :learning_object, :resumable_filename, :concatenated
def initialize(params)
@learning_object = LearningObject.find params[:id]
@concatenated = false
return nil if @learning_object.blank?
@dir = "/tmp/#{params[:resumableIdentifier]}"
@resumable_filename = "#{@dir}/#{params[:resumableFilename]}"
@resumable_file_extension = File.extname(@resumable_filename)[1..-1]
# return nil unless valid_mime_type?
@total_chunks = params[:resumableTotalChunks].to_i
@chunk_number = params[:resumableChunkNumber].to_i
@chunk_size = params[:resumableChunkSize].to_i
@chunk_tmpfile = params[:file].tempfile unless params[:file].nil?
@chunk = resumable_chunk @chunk_number
end
def exist?
File.exist? @chunk
end
def save
# Create chunks directory when not present on system
FileUtils.mkdir(@dir, mode: 0700) unless File.directory?(@dir)
# Move the uploaded chunk to the directory
FileUtils.mv @chunk_tmpfile, @chunk
concatenate_chunks if all_parts_exist?
end
private
def valid_mime_type?
mime_types = @learning_object.object_type.mime_types.map(&:extension)
return true if mime_types.empty?
mime_types.include? @resumable_file_extension
end
def resumable_chunk(part)
"#{@resumable_filename}.part#{part}"
end
def concatenate_chunks
File.open(@resumable_filename, 'a') do |target|
1.upto(@total_chunks) do |i|
chunk = File.open(resumable_chunk(i), 'r').read
chunk.each_line { |line| target << line }
FileUtils.rm resumable_chunk(i), force: true
end
Rails.logger.info "File saved to #{@resumable_filename}"
end
@concatenated = true
end
def all_parts_exist?
@total_chunks.downto(1) do |i|
return false unless File.exist?(resumable_chunk(i))
end
true
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment