ruby - 用于活动存储的 FTP 服务
问题描述
我正在为 ActiveStorage https://github.com/gordienko/activestorage-ftp开发一项服务,该服务能够通过 FTP 协议下载和删除文件,并通过 Nginx 显示。以模块https://github.com/luan/carrierwave-ftp为例。
nginx Perl Module to Output Content-MD5 HTTP Header https://gist.github.com/sivel/1870822用于验证文件的校验和。
解决了下载,验证和删除文件的任务。重新定义 url 和 url_for_direct_upload 函数存在问题。
url_for_direct_upload 函数仍然使用 DiskService。
我在下面给出我的代码,我为很多代码道歉。
也许,这个想法对某人来说似乎很有趣。我很乐意接受任何意见或建议。
require "active_storage_ftp/ex_ftp"
require "active_storage_ftp/ex_ftptls"
require "digest/md5"
require "active_support/core_ext/numeric/bytes"
module ActiveStorage
class Service::FtpService < Service
def initialize(**config)
@config = config
end
def upload(key, io, checksum: nil, **)
instrument :upload, key: key, checksum: checksum do
connection do |ftp|
path_for(key).tap do |path|
ftp.mkdir_p(::File.dirname path)
ftp.chdir(::File.dirname path)
ftp.storbinary("STOR #{File.basename(key)}", io, Net::FTP::DEFAULT_BLOCKSIZE)
if ftp_chmod
ftp.sendcmd("SITE CHMOD #{ftp_chmod.to_s(8)} #{path_for(key)}")
end
end
end
ensure_integrity_of(key, checksum) if checksum
end
end
def download(key)
if block_given?
instrument :streaming_download, key: key do
open(http_url_for(key)) do |file|
while data = file.read(64.kilobytes)
yield data
end
end
end
else
instrument :download, key: key do
open(http_url_for(key)) do |file|
file.read
end
end
end
end
def download_chunk(key, range)
instrument :download_chunk, key: key, range: range do
open(http_url_for(key)) do |file|
file.seek range.begin
file.read range.size
end
end
end
def delete(key)
instrument :delete, key: key do
begin
connection do |ftp|
ftp.chdir(::File.dirname path_for(key))
ftp.delete(::File.basename path_for(key))
end
rescue
# Ignore files already deleted
end
end
end
def delete_prefixed(prefix)
instrument :delete_prefixed, prefix: prefix do
connection do |ftp|
ftp.chdir(path_for(prefix))
ftp.list.each do |file|
ftp.delete(file.split.last)
end
end
end
end
def exist?(key)
instrument :exist, key: key do |payload|
response = request_head(key)
answer = response.code.to_i == 200
payload[:exist] = answer
answer
end
end
def url(key, expires_in:, filename:, disposition:, content_type:)
instrument :url, key: key do |payload|
generated_url = http_url_for(key)
payload[:url] = generated_url
generated_url
end
end
def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
instrument :url, key: key do |payload|
verified_token_with_expiration = ActiveStorage.verifier.generate(
{
key: key,
content_type: content_type,
content_length: content_length,
checksum: checksum
},
{expires_in: expires_in,
purpose: :blob_token}
)
generated_url = url_helpers.update_rails_disk_service_url(verified_token_with_expiration, host: current_host)
payload[:url] = generated_url
generated_url
end
end
def headers_for_direct_upload(key, content_type:, **)
{"Content-Type" => content_type}
end
def path_for(key) #:nodoc:
File.join ftp_folder, folder_for(key), key
end
private
attr_reader :config
def folder_for(key)
[key[0..1], key[2..3]].join("/")
end
def ensure_integrity_of(key, checksum)
response = request_head(key)
unless "#{response['Content-MD5']}==" == checksum
delete key
raise ActiveStorage::IntegrityError
end
end
def url_helpers
@url_helpers ||= Rails.application.routes.url_helpers
end
def current_host
ActiveStorage::Current.host
end
def request_head(key)
uri = URI(http_url_for(key))
request = Net::HTTP.new(uri.host, uri.port)
request.use_ssl = uri.scheme == 'https'
request.request_head(uri.path)
end
def http_url_for(key)
([ftp_url, folder_for(key), key].join('/'))
end
def inferred_content_type
SanitizedFile.new(path).content_type
end
def ftp_host
config.fetch(:ftp_host)
end
def ftp_port
config.fetch(:ftp_port)
end
def ftp_user
config.fetch(:ftp_user)
end
def ftp_passwd
config.fetch(:ftp_passwd)
end
def ftp_folder
config.fetch(:ftp_folder)
end
def ftp_url
config.fetch(:ftp_url)
end
def ftp_passive
config.fetch(:ftp_passive)
end
def ftp_chmod
config.fetch(:ftp_chmod, 0600)
end
def connection
ftp = ExFTP.new
ftp.connect(ftp_host, ftp_port)
begin
ftp.passive = ftp_passive
ftp.login(ftp_user, ftp_passwd)
yield ftp
ensure
ftp.quit
end
end
end
end
解决方案
推荐阅读
- python-3.x - 以日历为输入生成日期范围
- python - 根据 Pandas 分组结果计算公式并添加到最终结果集中
- java - 从联系人中检索姓名时,仅检索我联系人中的最后一个人,所有其他人都为空
- azure-storage - Azure Policy (deployifnotexists) 未按预期运行
- r - 使用 geom_tile() 堆叠重叠的瓷砖
- python - 如何使用 pyenv 在 OSX 上安装新的 SSL 证书?
- python - Python 正则表达式查找子字符串和第一个空格之间的所有内容
- java - My App Crashes When I press A Button In Android Studio
- python - 从 BeautifulSoup 中的 Span 类中提取数据/价格
- firebase - Crashlytics 和 runZonedGuarded