activestorage_qinium 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a957256178e151a2f7cb7980ca1b5d14c2eb581f3aca737d08ef2b22fe3b9821
4
- data.tar.gz: 67ed36ff67c18a64f3c7c990993cf289e84726a90b0eed2a9720a2efe4037fd7
3
+ metadata.gz: '00099f76b5d04bf4937fa1a7ba08b6372c27438cb6a743b14b28c178f7be6d23'
4
+ data.tar.gz: 60cd2922c6d18ef2efe74b81445ec5a74046f33fd1ba3d269321642bf5fefa14
5
5
  SHA512:
6
- metadata.gz: 992c598c417a4cb89131e57345f476eed8d461a2a02aa89fbc47cca29951297193ae1e035ef6b1f036209a764b1995076e826e98de2877ee3cf2a09fa3238f95
7
- data.tar.gz: a208527fdd58e43fdeb7bf0304ac778a67aa8eec900ce5eca476b96e0f93c53028048b1a7964e2ac83f21eb3a7eb496b054036e2787f63740d7eac20a739217f
6
+ metadata.gz: 3c41a1e9a044866659fb3528d3a4d8ee5d93144e737e891fc8578aa70385ec72f405b531df0de8edf69b32e7f555e8686b7f30f23267b04f7ad3742ec3b1a852
7
+ data.tar.gz: 9d6c3a98ddf542e32def899fa36fc1f6f6566fa957aff73009016c19484acf92c838fa4d7b327a9448c08044d1a35ca6cbe509b08033ec3904166b897a7b7deb
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.2.1)
5
+ qinium (~> 0.2.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.2.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.2.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,7 @@ 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"] }
98
110
  end
99
111
  end
100
112
 
@@ -110,20 +122,21 @@ module ActiveStorage
110
122
  instrument :url, key: key do |payload|
111
123
  fop = if options[:fop].present? # 内容预处理
112
124
  options[:fop]
113
- elsif options[:disposition].to_s == 'attachment' # 下载附件
114
- attname = URI.encode_www_form_component "#{options[:filename] || key}"
125
+ elsif options[:disposition].to_s == "attachment" # 下载附件
126
+ attname = URI.encode_www_form_component (options[:filename] || key).to_s
115
127
  "attname=#{attname}"
116
128
  end
117
129
 
118
- url = if bucket_private
130
+ url = if config.public
131
+ url_encoded_key = key.split("/").map { |x| CGI.escape(x) }.join("/")
132
+ ["#{protocol}://#{domain}/#{url_encoded_key}", fop].compact.join("?")
133
+ else
119
134
  expires_in = options[:expires_in] ||
120
135
  Rails.application.config.active_storage.service_urls_expire_in ||
121
136
  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('?')
137
+ Qinium::Auth.authorize_download_url(domain, key,
138
+ access_key, secret_key,
139
+ schema: protocol, fop: fop, expires_in: expires_in)
127
140
  end
128
141
 
129
142
  payload[:url] = url
@@ -133,9 +146,9 @@ module ActiveStorage
133
146
 
134
147
  private
135
148
 
136
- def items_for(prefix = '')
149
+ def items_for(prefix = "")
137
150
  _code, data, _headers = qiniu.object.list(prefix: prefix)
138
- data['items']
151
+ data["items"]
139
152
  end
140
153
 
141
154
  def upload_blk(blk, token:, host: nil)
@@ -147,8 +160,9 @@ module ActiveStorage
147
160
 
148
161
  def with_retries(max: 3)
149
162
  yield
150
- rescue
163
+ rescue StandardError
151
164
  raise if max.zero?
165
+
152
166
  max -= 1
153
167
  retry
154
168
  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.2.1"
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.2.1
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-09 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.2.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.2.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