Class: SkadateGems::Dev::Remote::File

Inherits:
Object
  • Object
show all
Defined in:
lib/skadategems/dev/remote/file.rb

Overview

Remote file accessor.

Direct Known Subclasses

Directory

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(remote, path, size = nil) ⇒ File

Returns a new instance of File.



19
20
21
# File 'lib/skadategems/dev/remote/file.rb', line 19

def initialize(remote, path, size = nil)
  @remote, @path, @size = remote, path, size
end

Instance Attribute Details

#pathString (readonly)

Returns relative path to a file.

Returns:

  • (String)

    relative path to a file



14
15
16
# File 'lib/skadategems/dev/remote/file.rb', line 14

def path
  @path
end

#sizeFixnum (readonly)

Returns remote file size in bytes.

Returns:

  • (Fixnum)

    remote file size in bytes



17
18
19
# File 'lib/skadategems/dev/remote/file.rb', line 17

def size
  @size
end

Instance Method Details

#basenameString

Returns remote file basename.

Returns:

  • (String)

    remote file basename



28
29
30
# File 'lib/skadategems/dev/remote/file.rb', line 28

def basename
  ::File.basename(@path)
end

#directory?Boolean

Returns:

  • (Boolean)


23
24
25
# File 'lib/skadategems/dev/remote/file.rb', line 23

def directory?
  false
end

#extnameString

Returns remote file extension.

Returns:

  • (String)

    remote file extension



33
34
35
# File 'lib/skadategems/dev/remote/file.rb', line 33

def extname
  ::File.extname(@path)
end

#friendly_sizeString

Returns human readable file size.

Returns:

  • (String)

    human readable file size



38
39
40
41
42
43
44
45
46
# File 'lib/skadategems/dev/remote/file.rb', line 38

def friendly_size
  units = %W(B KiB MiB GiB TiB)

  size, unit = units.reduce(@size.to_f) do |(fsize, _), type|
    fsize > 512 ? [fsize / 1024, type] : (break [fsize, type])
  end

  "#{size > 9 || size.modulo(1) < 0.1 ? '%d' : '%.1f'} %s" % [size, unit]
end

#request(md5_checksum: nil) {|response| ... } ⇒ Object

Request remote file to read it’s contents.

Yields:

  • (response)

    passes a http response to the block



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/skadategems/dev/remote/file.rb', line 50

def request(md5_checksum: nil, &block)
  path = self.path.dup
  path[0] = '' if path[0] == '/'

  batch = ScriptBatch.new

  batch << <<-PHPSCRIPT
$filename = DIR_SITE_ROOT . '#{path.gsub(/['\\]/, '\\\\\0')}';

if (!file_exists($filename)) {
  header('HTTP/1.1 404 Not Found');
  exit;
}

$md5_checksum = md5_file($filename);
  PHPSCRIPT

  if md5_checksum
    raise ':md5_checksum must be a String' unless md5_checksum.is_a? String

    batch << <<-PHPSCRIPT
if ($md5_checksum === '#{md5_checksum.gsub(/['\\]/, '\\\\\0')}') {
  header('HTTP/1.1 304 Not Modified');
  exit;
}
    PHPSCRIPT
  end

  batch << <<-PHPSCRIPT
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . basename($filename));
header('Content-Length: ' . filesize($filename));
header('X-Content-MD5-Checksum: ' . $md5_checksum);
ob_clean();
flush();
readfile($filename);
exit;
  PHPSCRIPT

  @remote.accessor.exec(batch) do |response|
    if response.is_a?(Net::HTTPNotModified)
      block.call(response)
    elsif response.is_a?(Net::HTTPOK)
      if response['content-disposition'] == "attachment; filename=#{basename}" &&
          response['x-content-md5-checksum']
        block.call(response)
      else
        raise 'unexpected response headers'
      end
    else
      raise "[#{response.code} \"#{response.message}\"]"
    end
  end
end

#save_as(filename) {|chunk_size| ... } ⇒ Object

Download and save a remote file as a given filename.

Parameters:

  • filename (String)

    filename to save file as

Yields:

  • (chunk_size)

    on each segment receive

Yield Parameters:

  • chunk_size (Fixnum)

    received segment size



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/skadategems/dev/remote/file.rb', line 110

def save_as(filename)
  current_checksum = if ::File.exists?(filename)
    Digest::MD5.file(filename).hexdigest
  end

  request(md5_checksum: current_checksum) do |response|
    if response.is_a?(Net::HTTPNotModified)
      return false
    elsif response.is_a?(Net::HTTPOK)
      @size = response['content-length'].to_i if @size.nil?
      new_checksum = response['x-content-md5-checksum']
      md5 = Digest::MD5.new

      begin
        tempfile = Tempfile.create([basename, extname])
        response.read_body do |chunk|
          tempfile << chunk
          md5 << chunk
          yield chunk.size if block_given?
        end
        tempfile.close

        if md5.hexdigest != new_checksum
          raise "md5 checksum mismatch:\n  [#{md5.hexdigest}]\n  [#{new_checksum}]"
        end

        parent_dir = ::File.dirname(filename)
        FileUtils.mkdir_p(parent_dir) unless ::File.exists?(parent_dir)

        FileUtils.mv(tempfile.path, filename)
        FileUtils.chmod(0644, filename)

        return true
      ensure
        tempfile.close unless tempfile.closed?
      end
    else
      raise "unexpected response #{response}"
    end
  end
end