activestorage_qinium 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a957256178e151a2f7cb7980ca1b5d14c2eb581f3aca737d08ef2b22fe3b9821
4
- data.tar.gz: 67ed36ff67c18a64f3c7c990993cf289e84726a90b0eed2a9720a2efe4037fd7
3
+ metadata.gz: 7bb9c6ef6afef08f075c68b456b97cf3baae4a4e0a359387f25ff76ed808e4bf
4
+ data.tar.gz: 8cdcb99e4f67c3062e8e82ff54abfca70bce81883557c2a1efa0b3d130d63d3b
5
5
  SHA512:
6
- metadata.gz: 992c598c417a4cb89131e57345f476eed8d461a2a02aa89fbc47cca29951297193ae1e035ef6b1f036209a764b1995076e826e98de2877ee3cf2a09fa3238f95
7
- data.tar.gz: a208527fdd58e43fdeb7bf0304ac778a67aa8eec900ce5eca476b96e0f93c53028048b1a7964e2ac83f21eb3a7eb496b054036e2787f63740d7eac20a739217f
6
+ metadata.gz: 38ddca081df7e934137337ba619755c13788538dfb073f1a77f27789d6347e8bea77f5811b403d0de7d924c35e7b7b352398202efcdab59cddc998488f176d07
7
+ data.tar.gz: 174f234db5c018b6a65bb09c3c9979b10dcddf56b8772f9248f626bba3e90e321da2d9fc0c581fdddb4e35ba6d1b6bb12aa1cbf3b860f9d41561022f02043c5a
data/CHANGELOG.md CHANGED
@@ -3,3 +3,7 @@
3
3
  ## [0.1.0] - 2022-08-10
4
4
 
5
5
  - Initial release
6
+
7
+ ## [0.2.1] - 2024-05-09
8
+
9
+ - Support private download url
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activestorage_qinium (0.1.0)
5
- qinium (~> 0.1.1)
4
+ activestorage_qinium (0.3.0)
5
+ qinium (~> 0.3.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
@@ -12,7 +12,7 @@ GEM
12
12
  parallel (1.22.1)
13
13
  parser (3.1.1.0)
14
14
  ast (~> 2.4.1)
15
- qinium (0.1.1)
15
+ qinium (0.3.0)
16
16
  rainbow (3.1.1)
17
17
  rake (13.0.6)
18
18
  regexp_parser (2.5.0)
@@ -15,11 +15,9 @@ Gem::Specification.new do |spec|
15
15
  spec.required_ruby_version = ">= 2.6.0"
16
16
 
17
17
  spec.metadata["homepage_uri"] = spec.homepage
18
- spec.metadata["source_code_uri"] =spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
19
  spec.metadata["changelog_uri"] = "#{spec.homepage}/CHANGELOG.md"
20
20
 
21
- # Specify which files should be added to the gem when it is released.
22
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
21
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
22
  `git ls-files -z`.split("\x0").reject do |f|
25
23
  (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
@@ -28,5 +26,5 @@ Gem::Specification.new do |spec|
28
26
 
29
27
  spec.require_paths = ["lib"]
30
28
 
31
- spec.add_dependency "qinium", "~> 0.1.1"
29
+ spec.add_dependency "qinium", "~> 0.3.0"
32
30
  end
@@ -1,10 +1,12 @@
1
+ require "open-uri"
2
+ require "active_storage/analyzer/qinium_image_analyzer"
1
3
  module ActiveStorage
2
4
  class Service::QiniumService < Service
3
5
  attr_reader :qiniu
4
6
 
5
7
  delegate :config, :client, to: :qiniu
6
- delegate :settings, :bucket_private, :bucket, :access_key, :secret_key, :domain,
7
- :protocol, :put_policy_options,
8
+ delegate :settings, :bucket, :access_key, :secret_key, :domain,
9
+ :protocol, :put_policy_options,
8
10
  to: :config
9
11
 
10
12
  def self.analyzers
@@ -15,7 +17,7 @@ module ActiveStorage
15
17
  @qiniu = Qinium.new(options)
16
18
  end
17
19
 
18
- def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
20
+ def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:, custom_metadata:)
19
21
  instrument :url, key: key do |payload|
20
22
  url = config.up_host
21
23
  payload[:url] = url
@@ -23,15 +25,25 @@ module ActiveStorage
23
25
  end
24
26
  end
25
27
 
26
- def form_data_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
28
+ def http_method_for_direct_upload
29
+ "POST"
30
+ end
31
+
32
+ def http_response_type_for_direct_upload
33
+ "json"
34
+ end
35
+
36
+ def form_data_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:, **)
27
37
  put_policy = Qinium::PutPolicy.new(config, key: key, expires_in: expires_in)
28
38
  put_policy.fsize_limit = content_length.to_i + 1000
29
- put_policy.mime_limit = content_type
39
+ # OPTIMIZE: 暂时关闭文件类型限制,避免 xmind 文件无法上传
40
+ put_policy.mime_limit = nil
30
41
  put_policy.detect_mime = 1
31
42
  put_policy.insert_only = 1
32
43
  {
33
44
  key: key,
34
- token: put_policy.to_token
45
+ token: put_policy.to_token,
46
+ ':file': "file"
35
47
  }
36
48
  end
37
49
 
@@ -46,19 +58,19 @@ module ActiveStorage
46
58
  host = nil
47
59
  while (blk = io.read(config.block_size))
48
60
  data = upload_blk(blk, token: up_token, host: host)
49
- ctx = data.fetch('ctx')
50
- host = data.fetch('host')
61
+ ctx = data.fetch("ctx")
62
+ host = data.fetch("host")
51
63
  file_size += blk.size
52
64
  blocks.push(ctx)
53
65
  end
54
66
 
55
- _code, data, _headers = qiniu.object.mkfile(token: up_token, file_size: file_size, key: key, mime_type: content_type, blocks: blocks)
67
+ _code, data, _headers = qiniu.object.mkfile(token: up_token, file_size: file_size, key: key,
68
+ mime_type: content_type, blocks: blocks)
56
69
  data
57
70
  end
58
71
  end
59
72
 
60
- def update_metadata(key, **metadata)
61
- end
73
+ def update_metadata(key, **metadata); end
62
74
 
63
75
  def download(key)
64
76
  if block_given?
@@ -81,7 +93,7 @@ module ActiveStorage
81
93
  uri = URI(url(key, disposition: :attachment))
82
94
  Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |client|
83
95
  client.get(uri,
84
- 'Range' => "bytes=#{range.begin}-#{range.exclude_end? ? range.end - 1 : range.end}").body
96
+ "Range" => "bytes=#{range.begin}-#{range.exclude_end? ? range.end - 1 : range.end}").body
85
97
  end
86
98
  end
87
99
  end
@@ -94,7 +106,13 @@ module ActiveStorage
94
106
 
95
107
  def delete_prefixed(prefix)
96
108
  instrument :delete_prefixed, prefix: prefix do
97
- items_for(prefix).each { |item| delete item['key'] }
109
+ items_for(prefix).each { |item| delete item["key"] }
110
+ end
111
+ end
112
+
113
+ def fetch(target_url, key)
114
+ instrument :fetch, target_url: target_url, key: key do
115
+ qiniu.object.fetch(target_url, key)
98
116
  end
99
117
  end
100
118
 
@@ -110,20 +128,21 @@ module ActiveStorage
110
128
  instrument :url, key: key do |payload|
111
129
  fop = if options[:fop].present? # 内容预处理
112
130
  options[:fop]
113
- elsif options[:disposition].to_s == 'attachment' # 下载附件
114
- attname = URI.encode_www_form_component "#{options[:filename] || key}"
131
+ elsif options[:disposition].to_s == "attachment" # 下载附件
132
+ attname = URI.encode_www_form_component (options[:filename] || key).to_s
115
133
  "attname=#{attname}"
116
134
  end
117
135
 
118
- url = if bucket_private
136
+ url = if config.public
137
+ url_encoded_key = key.split("/").map { |x| CGI.escape(x) }.join("/")
138
+ ["#{protocol}://#{domain}/#{url_encoded_key}", fop].compact.join("?")
139
+ else
119
140
  expires_in = options[:expires_in] ||
120
141
  Rails.application.config.active_storage.service_urls_expire_in ||
121
142
  3600
122
- qiniu.auth.authorize_download_url(settings, domain, key,
123
- schema: protocol, fop: fop, expires_in: expires_in)
124
- else
125
- url_encoded_key = key.split('/').map { |x| CGI.escape(x) }.join('/')
126
- ["#{protocol}://#{domain}/#{url_encoded_key}", fop].compact.join('?')
143
+ Qinium::Auth.authorize_download_url(domain, key,
144
+ access_key, secret_key,
145
+ schema: protocol, fop: fop, expires_in: expires_in)
127
146
  end
128
147
 
129
148
  payload[:url] = url
@@ -133,9 +152,9 @@ module ActiveStorage
133
152
 
134
153
  private
135
154
 
136
- def items_for(prefix = '')
155
+ def items_for(prefix = "")
137
156
  _code, data, _headers = qiniu.object.list(prefix: prefix)
138
- data['items']
157
+ data["items"]
139
158
  end
140
159
 
141
160
  def upload_blk(blk, token:, host: nil)
@@ -147,8 +166,9 @@ module ActiveStorage
147
166
 
148
167
  def with_retries(max: 3)
149
168
  yield
150
- rescue
169
+ rescue StandardError
151
170
  raise if max.zero?
171
+
152
172
  max -= 1
153
173
  retry
154
174
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveStorageQinium
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "active_storage_qinium/version"
4
-
4
+ require 'qinium'
5
5
  module ActiveStorageQinium
6
6
 
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activestorage_qinium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - xiaohui
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-11 00:00:00.000000000 Z
11
+ date: 2024-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: qinium
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.1.1
19
+ version: 0.3.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.1.1
26
+ version: 0.3.0
27
27
  description: Wraps the Qiniu Storage Service as an Active Storage service, support
28
28
  muti-tenant settings. https://www.qiniu.com
29
29
  email:
@@ -41,16 +41,9 @@ files:
41
41
  - README.md
42
42
  - Rakefile
43
43
  - activestorage_qinium.gemspec
44
- - app/javascript/active_storage_qinium/direct_upload_controller.js
45
- - app/javascript/active_storage_qinium/direct_upload_controller/blob_record.js
46
- - app/javascript/active_storage_qinium/direct_upload_controller/blob_upload.js
47
- - app/javascript/active_storage_qinium/direct_upload_controller/direct_upload.js
48
- - app/javascript/active_storage_qinium/direct_upload_controller/file_checksum.js
49
- - app/javascript/active_storage_qinium/direct_upload_controller/helpers.js
50
44
  - lib/active_storage/analyzer/qinium_image_analyzer.rb
51
45
  - lib/active_storage/service/qinium_service.rb
52
46
  - lib/active_storage_qinium.rb
53
- - lib/active_storage_qinium/engine.rb
54
47
  - lib/active_storage_qinium/version.rb
55
48
  - lib/activestorage_qinium.rb
56
49
  - sig/activestorage_qinium.rbs
@@ -76,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
69
  - !ruby/object:Gem::Version
77
70
  version: '0'
78
71
  requirements: []
79
- rubygems_version: 3.3.7
72
+ rubygems_version: 3.4.19
80
73
  signing_key:
81
74
  specification_version: 4
82
75
  summary: A muti-tenant SDK wrap the Qiniu Storage Service as an Active Storage service
@@ -1,73 +0,0 @@
1
- import { getMetaValue } from "./helpers"
2
-
3
- export class BlobRecord {
4
- constructor(file, checksum, url) {
5
- this.file = file
6
-
7
- this.attributes = {
8
- filename: file.name,
9
- content_type: file.type || "application/octet-stream",
10
- byte_size: file.size,
11
- checksum: checksum
12
- }
13
-
14
- this.xhr = new XMLHttpRequest
15
- this.xhr.open("POST", url, true)
16
- this.xhr.responseType = "json"
17
- this.xhr.setRequestHeader("Content-Type", "application/json")
18
- this.xhr.setRequestHeader("Accept", "application/json")
19
- this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
20
-
21
- const csrfToken = getMetaValue("csrf-token")
22
- if (csrfToken != undefined) {
23
- this.xhr.setRequestHeader("X-CSRF-Token", csrfToken)
24
- }
25
-
26
- this.xhr.addEventListener("load", event => this.requestDidLoad(event))
27
- this.xhr.addEventListener("error", event => this.requestDidError(event))
28
- }
29
-
30
- get status() {
31
- return this.xhr.status
32
- }
33
-
34
- get response() {
35
- const { responseType, response } = this.xhr
36
- if (responseType == "json") {
37
- return response
38
- } else {
39
- // Shim for IE 11: https://connect.microsoft.com/IE/feedback/details/794808
40
- return JSON.parse(response)
41
- }
42
- }
43
-
44
- create(callback) {
45
- this.callback = callback
46
- this.xhr.send(JSON.stringify({ blob: this.attributes }))
47
- }
48
-
49
- requestDidLoad(event) {
50
- if (this.status >= 200 && this.status < 300) {
51
- const { response } = this
52
- const { direct_upload } = response
53
- delete response.direct_upload
54
- this.attributes = response
55
- this.directUploadData = direct_upload
56
- this.callback(null, this.toJSON())
57
- } else {
58
- this.requestDidError(event)
59
- }
60
- }
61
-
62
- requestDidError(event) {
63
- this.callback(event, this)
64
- }
65
-
66
- toJSON() {
67
- const result = {}
68
- for (const key in this.attributes) {
69
- result[key] = this.attributes[key]
70
- }
71
- return result
72
- }
73
- }
@@ -1,45 +0,0 @@
1
- export class BlobUpload {
2
- constructor(blob) {
3
- this.blob = blob
4
- this.file = blob.file
5
- this.dataBuilder = blob.dataBuilder || ((ctx) => ctx.file.slice())
6
- const { url, headers } = blob.directUploadData
7
- this.xhr = new XMLHttpRequest
8
- this.xhr.open("POST", url, true)
9
- // this.xhr.responseType = "text"
10
- for (const key in headers) {
11
- this.xhr.setRequestHeader(key, headers[key])
12
- }
13
- this.xhr.addEventListener("load", event => this.requestDidLoad(event))
14
- this.xhr.addEventListener("error", event => this.requestDidError(event))
15
- }
16
-
17
- create(callback) {
18
- // debugger
19
- this.callback = callback
20
- if(this.blob.directUploadData.formData){
21
- var formData
22
- formData = new FormData()
23
- for(const key in this.blob.directUploadData.formData){
24
- formData.append(key, this.blob.directUploadData.formData[key])
25
- }
26
- formData.append('file', this.file)
27
- this.xhr.send(formData)
28
- }else{
29
- this.xhr.send(this.dataBuilder(this.blob))
30
- }
31
- }
32
-
33
- requestDidLoad(event) {
34
- const { status, response } = this.xhr
35
- if (status >= 200 && status < 300) {
36
- this.callback(null, response)
37
- } else {
38
- this.requestDidError(event)
39
- }
40
- }
41
-
42
- requestDidError(event) {
43
- this.callback(event, this)
44
- }
45
- }
@@ -1,48 +0,0 @@
1
- import { FileChecksum } from "./file_checksum"
2
- import { BlobRecord } from "./blob_record"
3
- import { BlobUpload } from "./blob_upload"
4
-
5
- let id = 0
6
-
7
- export class DirectUpload {
8
- constructor(file, url, delegate) {
9
- this.id = ++id
10
- this.file = file
11
- this.url = url
12
- this.delegate = delegate
13
- }
14
-
15
- create(callback) {
16
- FileChecksum.create(this.file, (error, checksum) => {
17
- if (error) {
18
- callback(error)
19
- return
20
- }
21
-
22
- const blob = new BlobRecord(this.file, checksum, this.url)
23
- notify(this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr)
24
-
25
- blob.create(error => {
26
- if (error) {
27
- callback(error)
28
- } else {
29
- const upload = new BlobUpload(blob)
30
- notify(this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr)
31
- upload.create(error => {
32
- if (error) {
33
- callback(error)
34
- } else {
35
- callback(null, blob.toJSON())
36
- }
37
- })
38
- }
39
- })
40
- })
41
- }
42
- }
43
-
44
- function notify(object, methodName, ...messages) {
45
- if (object && typeof object[methodName] == "function") {
46
- return object[methodName](...messages)
47
- }
48
- }
@@ -1,53 +0,0 @@
1
- import SparkMD5 from "spark-md5"
2
-
3
- const fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
4
-
5
- export class FileChecksum {
6
- static create(file, callback) {
7
- const instance = new FileChecksum(file)
8
- instance.create(callback)
9
- }
10
-
11
- constructor(file) {
12
- this.file = file
13
- this.chunkSize = 2097152 // 2MB
14
- this.chunkCount = Math.ceil(this.file.size / this.chunkSize)
15
- this.chunkIndex = 0
16
- }
17
-
18
- create(callback) {
19
- this.callback = callback
20
- this.md5Buffer = new SparkMD5.ArrayBuffer
21
- this.fileReader = new FileReader
22
- this.fileReader.addEventListener("load", event => this.fileReaderDidLoad(event))
23
- this.fileReader.addEventListener("error", event => this.fileReaderDidError(event))
24
- this.readNextChunk()
25
- }
26
-
27
- fileReaderDidLoad(event) {
28
- this.md5Buffer.append(event.target.result)
29
-
30
- if (!this.readNextChunk()) {
31
- const binaryDigest = this.md5Buffer.end(true)
32
- const base64digest = btoa(binaryDigest)
33
- this.callback(null, base64digest)
34
- }
35
- }
36
-
37
- fileReaderDidError(event) {
38
- this.callback(`Error reading ${this.file.name}`)
39
- }
40
-
41
- readNextChunk() {
42
- if (this.chunkIndex < this.chunkCount || (this.chunkIndex == 0 && this.chunkCount == 0)) {
43
- const start = this.chunkIndex * this.chunkSize
44
- const end = Math.min(start + this.chunkSize, this.file.size)
45
- const bytes = fileSlice.call(this.file, start, end)
46
- this.fileReader.readAsArrayBuffer(bytes)
47
- this.chunkIndex++
48
- return true
49
- } else {
50
- return false
51
- }
52
- }
53
- }
@@ -1,51 +0,0 @@
1
- export function getMetaValue(name) {
2
- const element = findElement(document.head, `meta[name="${name}"]`)
3
- if (element) {
4
- return element.getAttribute("content")
5
- }
6
- }
7
-
8
- export function findElements(root, selector) {
9
- if (typeof root == "string") {
10
- selector = root
11
- root = document
12
- }
13
- const elements = root.querySelectorAll(selector)
14
- return toArray(elements)
15
- }
16
-
17
- export function findElement(root, selector) {
18
- if (typeof root == "string") {
19
- selector = root
20
- root = document
21
- }
22
- return root.querySelector(selector)
23
- }
24
-
25
- export function dispatchEvent(element, type, eventInit = {}) {
26
- const { disabled } = element
27
- const { bubbles, cancelable, detail } = eventInit
28
- const event = document.createEvent("Event")
29
-
30
- event.initEvent(type, bubbles || true, cancelable || true)
31
- event.detail = detail || {}
32
-
33
- try {
34
- element.disabled = false
35
- element.dispatchEvent(event)
36
- } finally {
37
- element.disabled = disabled
38
- }
39
-
40
- return event
41
- }
42
-
43
- export function toArray(value) {
44
- if (Array.isArray(value)) {
45
- return value
46
- } else if (Array.from) {
47
- return Array.from(value)
48
- } else {
49
- return [].slice.call(value)
50
- }
51
- }
@@ -1,55 +0,0 @@
1
- import { Controller } from "stimulus";
2
- import { DirectUpload } from "./direct_upload_controller/direct_upload";
3
-
4
- export default class extends Controller {
5
- static targets = ['file'];
6
- static values = {
7
- 'url': String
8
- }
9
-
10
- initialize(){
11
- this.onFileChange = this.onFileChange.bind(this)
12
- }
13
-
14
- connect(){
15
- this.hiddenInput = document.createElement("input")
16
- this.hiddenInput.type = "hidden"
17
- this.hiddenInput.name = this.fileTarget.name
18
- this.fileTarget.removeAttribute('name')
19
- this.fileTarget.insertAdjacentElement("beforebegin", this.hiddenInput)
20
- this.fileTarget.addEventListener('change', this.onFileChange)
21
- }
22
-
23
- disconnect(){
24
- this.fileTarget.removeEventListener('change', this.onFileChange)
25
- }
26
-
27
- onFileChange(event){
28
- const { target } = event
29
- const { files } = target
30
- const directUpload = new DirectUpload(files[0], this.urlValue, this)
31
-
32
- directUpload.create((error, attributes) => {
33
- if(error){
34
- this.hiddenInput.removeAttribute('value')
35
- }else{
36
- this.hiddenInput.setAttribute('value', attributes.signed_id)
37
- }
38
- })
39
- }
40
-
41
- // DirectUpload delegate
42
-
43
- directUploadWillCreateBlobWithXHR(xhr) {
44
- // this.dispatch("before-blob-request", { xhr })
45
- }
46
-
47
- directUploadWillStoreFileWithXHR(xhr) {
48
- // this.dispatch("before-storage-request", { xhr })
49
- xhr.upload.addEventListener("progress", event => this.uploadRequestDidProgress(event))
50
- }
51
-
52
- uploadRequestDidProgress(event){
53
-
54
- }
55
- }
@@ -1,4 +0,0 @@
1
- module ActiveStorageQinium
2
- class Engine < Rails::Engine
3
- end
4
- end