require 'zip' class ZipdownController < ApplicationController #查找项目(课程) before_filter :find_project_by_bid_id, :only => [:assort] #检查权限 #勿删 before_filter :authorize, :only => [:assort,:download_user_homework] SAVE_FOLDER = "#{Rails.root}/files" OUTPUT_FOLDER = "#{Rails.root}/files/archiveZip" #统一下载功能 def download begin send_file "#{OUTPUT_FOLDER}/#{params[:file]}", :filename => params[:filename], :type => detect_content_type(params[:file]) rescue => e render file: 'public/no_file_found.html' end end #一个作业下所有文件打包下载,只有admin和课程老师有权限 def assort if params[:obj_class] == "Bid" bid = Bid.find params[:obj_id] render_403 if User.current.allowed_to?(:as_teacher,bid.courses.first) file_count = 0 bid.homeworks.map { |homework| file_count += homework.attachments.count} if file_count > 0 zipfile = zip_bid bid else render file: 'public/no_file_found.html' return end else logger.error "[ZipDown#assort] ===> #{params[:obj_class]} unKown !!" end # if zipfile # if zipfile.length > 1 # @mut_down_files = zipfile #zipfile.each{|x| File.basename(x)} # else # send_file zipfile.first[:real_file], :filename => bid.name + ".zip", :type => detect_content_type(zipfile.first[:real_file]) # return # end # end respond_to do |format| format.json { render json: zipfile.to_json } end #rescue Exception => e # render file: 'public/no_file_found.html' end #下载某一学生的作业的所有文件 def download_user_homework homework = HomeworkAttach.find params[:homework] if User.current.admin? || User.current.member_of_course?(homework.bid.courses.first) if homework != nil unless homework.attachments.empty? zipfile = zip_homework_by_user homework send_file zipfile.file_path, :filename => ((homework.user.user_extensions.nil? || homework.user.user_extensions.student_id.nil?) ? "" : homework.user.user_extensions.student_id) + "_" + (homework.user.lastname.nil? ? "" : homework.user.lastname) + (homework.user.firstname.nil? ? "" : homework.user.firstname) + "_" + homework.name + ".zip", :type => detect_content_type(zipfile.file_path) if(zipfile) else render file: 'public/no_file_found.html' end else render file: 'public/file_not_found.html' end else render_403 end #rescue => e # render file: 'public/file_not_found.html' end private #通过作业Id找到项目(课程) def find_project_by_bid_id obj_class = params[:obj_class] obj_id = params[:obj_id] obj = obj_class.constantize.find(obj_id) case obj.class.to_s.to_sym when :Bid @project = obj.courses[0] end end def zip_bid(bid) # Todo: User Access Controll bid_homework_path = [] digests = [] bid.homeworks.each do |homeattach| unless homeattach.attachments.empty? out_file = zip_homework_by_user(homeattach) bid_homework_path << out_file.file_path digests << out_file.file_digest end end homework_id = bid.id user_id = bid.author_id out_file = find_or_pack(homework_id, user_id, digests.sort){ zipping("#{Time.now.to_i}_#{bid.name}.zip", bid_homework_path, OUTPUT_FOLDER) } # zips = split_pack_files(bid_homework_path, Setting.pack_attachment_max_size.to_i*1024) # x = 0 # # # zips.each { |o| # x += 1 # file = zipping "#{Time.now.to_i}_#{bid.name}_#{x}.zip", o[:files], OUTPUT_FOLDER # o[:real_file] = file # o[:file] = File.basename(file) # o[:size] = (File.size(file) / 1024.0 / 1024.0).round(2) # } [{files:[out_file.file_path], count: 1, index: 1, real_file: out_file.file_path, file: File.basename(out_file.file_path), size:(out_file.pack_size / 1024.0 / 1024.0).round(2) }] end def zip_homework_by_user(homework_attach) homeworks_attach_path = [] not_exist_file = [] # 需要将所有homework.attachments遍历加入zip digests = [] homework_attach.attachments.each do |attach| if File.exist?(attach.diskfile) homeworks_attach_path << attach.diskfile digests << attach.digest else not_exist_file << attach.filename digests << 'not_exist_file' end end out_file = find_or_pack(homework_attach.bid_id, homework_attach.user_id, digests.sort){ zipping("#{homework_attach.user.lastname}#{homework_attach.user.firstname}_#{((homework_attach.user.user_extensions.nil? || homework_attach.user.user_extensions.student_id.nil?) ? "" : homework_attach.user.user_extensions.student_id)}_#{Time.now.to_i.to_s}.zip", homeworks_attach_path, OUTPUT_FOLDER, true, not_exist_file) } end def find_or_pack(homework_id, user_id, digests) raise "please given a pack block" unless block_given? out_file = ZipPack.packed?(homework_id, user_id, digests.sort) unless out_file && out_file.file_valid? file = yield ZipPack.where(homework_id: homework_id, user_id: user_id).delete_all out_file = ZipPack.create(homework_id: homework_id, user_id: user_id, file_digest: Trustie::Utils.digest(file), file_path: file, pack_size: File.size(file), file_digests: digests.join(',') ) else out_file.pack_times = out_file.pack_times + 1 out_file.save end out_file end def zipping(zip_name_refer, files_paths, output_path, is_attachment=false, not_exist_file=[]) rename_zipfile = zip_name_refer ||= "#{Time.now.to_i.to_s}.zip" zipfile_name = "#{output_path}/#{rename_zipfile}" Dir.mkdir(File.dirname(zipfile_name)) unless File.exist?(File.dirname(zipfile_name)) Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile| files_paths.each do |filename| rename_file = File.basename(filename) rename_file = filename_to_real( File.basename(filename)) if is_attachment begin zipfile.add(rename_file, filename) rescue Exception => e zipfile.get_output_stream('FILE_NOTICE.txt'){|os| os.write l(:label_file_exist)} next end end unless not_exist_file.empty? zipfile.get_output_stream('FILE_LOST.txt'){|os| os.write l(:label_file_lost) + not_exist_file.join(',').to_s} end end zipfile_name end # 合理分配文件打包 # 如果小于 pack_attachment_max_size, 则返回单个文件 # 反之则切分为多个文件组返回 def split_pack_files(files, pack_attachment_max_size) max_size = 0 last_files = [] ret_files = [] files.each_with_index do |f,i| if (max_size += File.size(f)) > pack_attachment_max_size max_size = 0 if last_files.empty? #如果单个文件超过大小,也将此文件作为一组 ret_files << {files: [f], count: 1, index: ret_files.count+1} last_files.clear else ret_files << {files:last_files, count: last_files.count, index: ret_files.count+1} last_files.clear redo end else last_files << f end end ret_files << {files:last_files, count: last_files.count, index: ret_files.count+1} unless last_files.empty? ret_files end def detect_content_type(name) content_type = Redmine::MimeType.of(name) content_type.to_s end def filename_to_real(name) attach = Attachment.find_by_disk_filename(name) attach.filename end end