|  |  | 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
 | 
						
						
						
							|  |  |     if User.current.logged?
 | 
						
						
						
							|  |  |       begin
 | 
						
						
						
							|  |  |         send_file "#{OUTPUT_FOLDER}/#{params[:file]}", :filename => filename_for_content_disposition(params[:filename]), :type => detect_content_type(params[:file])
 | 
						
						
						
							|  |  |       rescue => e
 | 
						
						
						
							|  |  |         render file: 'public/no_file_found.html'
 | 
						
						
						
							|  |  |       end
 | 
						
						
						
							|  |  |     else
 | 
						
						
						
							|  |  |       render_403
 | 
						
						
						
							|  |  |     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
 | 
						
						
						
							|  |  |         zipfile = {:message => "no file"}
 | 
						
						
						
							|  |  |       end
 | 
						
						
						
							|  |  |     elsif params[:obj_class] == "HomeworkCommon"
 | 
						
						
						
							|  |  |       homework = HomeworkCommon.find params[:obj_id]
 | 
						
						
						
							|  |  |       render_403 if User.current.allowed_to?(:as_teacher,homework.course)
 | 
						
						
						
							|  |  |       file_count = 0
 | 
						
						
						
							|  |  |       homework.student_works.map { |work| file_count += work.attachments.count}
 | 
						
						
						
							|  |  |       if file_count > 0
 | 
						
						
						
							|  |  |         zipfile = zip_homework_common homework
 | 
						
						
						
							|  |  |       else
 | 
						
						
						
							|  |  |         zipfile = {:message => "no file"}
 | 
						
						
						
							|  |  |       end
 | 
						
						
						
							|  |  |     else
 | 
						
						
						
							|  |  |       logger.error "[ZipDown#assort] ===> #{params[:obj_class]} unKown !!"
 | 
						
						
						
							|  |  |     end
 | 
						
						
						
							|  |  |     respond_to do |format|
 | 
						
						
						
							|  |  |       format.json {
 | 
						
						
						
							|  |  |         render json: zipfile.to_json
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     end
 | 
						
						
						
							|  |  |   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
 | 
						
						
						
							|  |  |           filename = ((homework.user.user_extensions.nil? || homework.user.user_extensions.student_id.nil?) ? "" : homework.user.user_extensions.student_id) +
 | 
						
						
						
							|  |  |             "_" + homework.user.show_name +
 | 
						
						
						
							|  |  |             "_" + homework.name + ".zip"
 | 
						
						
						
							|  |  |           send_file zipfile.file_path, :filename => filename_for_content_disposition(filename), :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)
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  |     [{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_common homework_common
 | 
						
						
						
							|  |  |     bid_homework_path = []
 | 
						
						
						
							|  |  |     digests = []
 | 
						
						
						
							|  |  |     homework_common.student_works.each do |work|
 | 
						
						
						
							|  |  |       unless work.attachments.empty?
 | 
						
						
						
							|  |  |         out_file = zip_student_work_by_user(work)
 | 
						
						
						
							|  |  |         bid_homework_path << out_file.file_path
 | 
						
						
						
							|  |  |         digests << out_file.file_digest
 | 
						
						
						
							|  |  |       end
 | 
						
						
						
							|  |  |     end
 | 
						
						
						
							|  |  |     homework_id = homework_common.id
 | 
						
						
						
							|  |  |     user_id = homework_common.user_id
 | 
						
						
						
							|  |  |     out_file = find_or_pack(homework_id, user_id, digests.sort){
 | 
						
						
						
							|  |  |       zipping("#{Time.now.to_i}_#{homework_common.name}.zip",
 | 
						
						
						
							|  |  |               bid_homework_path, OUTPUT_FOLDER)
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  |     [{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.show_name}_#{((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 zip_student_work_by_user work
 | 
						
						
						
							|  |  |     homeworks_attach_path = []
 | 
						
						
						
							|  |  |     not_exist_file = []
 | 
						
						
						
							|  |  |     # 需要将所有homework.attachments遍历加入zip
 | 
						
						
						
							|  |  |     digests = []
 | 
						
						
						
							|  |  |     work.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(work.homework_common_id, work.user_id, digests.sort){
 | 
						
						
						
							|  |  |       zipping("#{work.user.show_name}_#{((work.user.user_extensions.nil? || work.user.user_extensions.student_id.nil?) ? "" : work.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
 |