Skip to content

Commit 33e717d

Browse files
committed
Load files from the s3 API
1 parent 4629c23 commit 33e717d

File tree

12 files changed

+125
-12
lines changed

12 files changed

+125
-12
lines changed

.github/workflows/ruby.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,16 @@ jobs:
1919
with:
2020
ruby-version: ${{ matrix.ruby }}
2121
bundler-cache: true
22+
- name: Start MinIO
23+
run: docker compose up -d minio
24+
- name: Wait for MinIO to be ready
25+
run: |
26+
timeout 60 bash -c 'until curl -f http://localhost:9000/minio/health/ready; do sleep 2; done'
2227
- name: Run tests
2328
run: bin/rake test:prepare spec
29+
- name: Stop MinIO
30+
if: always()
31+
run: docker compose down
2432
lint:
2533
runs-on: ubuntu-latest
2634
steps:

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,5 @@ gem 'connection_pool'
8282
group :production do
8383
gem 'newrelic_rpm'
8484
end
85+
86+
gem "aws-sdk-s3", "~> 1.199"

Gemfile.lock

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,25 @@ GEM
7777
airbrussh (1.5.3)
7878
sshkit (>= 1.6.1, != 1.7.0)
7979
ast (2.4.3)
80+
aws-eventstream (1.4.0)
81+
aws-partitions (1.1172.0)
82+
aws-sdk-core (3.233.0)
83+
aws-eventstream (~> 1, >= 1.3.0)
84+
aws-partitions (~> 1, >= 1.992.0)
85+
aws-sigv4 (~> 1.9)
86+
base64
87+
bigdecimal
88+
jmespath (~> 1, >= 1.6.1)
89+
logger
90+
aws-sdk-kms (1.113.0)
91+
aws-sdk-core (~> 3, >= 3.231.0)
92+
aws-sigv4 (~> 1.5)
93+
aws-sdk-s3 (1.199.1)
94+
aws-sdk-core (~> 3, >= 3.231.0)
95+
aws-sdk-kms (~> 1)
96+
aws-sigv4 (~> 1.5)
97+
aws-sigv4 (1.12.1)
98+
aws-eventstream (~> 1, >= 1.0.2)
8099
base64 (0.3.0)
81100
bcrypt_pbkdf (1.1.1)
82101
bcrypt_pbkdf (1.1.1-arm64-darwin)
@@ -186,6 +205,7 @@ GEM
186205
jbuilder (2.14.1)
187206
actionview (>= 7.0.0)
188207
activesupport (>= 7.0.0)
208+
jmespath (1.6.2)
189209
json (2.15.1)
190210
jwt (3.1.2)
191211
base64
@@ -422,6 +442,7 @@ PLATFORMS
422442
x86_64-linux
423443

424444
DEPENDENCIES
445+
aws-sdk-s3 (~> 1.199)
425446
bootsnap (>= 1.4.2)
426447
cancancan
427448
capistrano (~> 3.0)

app/controllers/file_controller.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
##
44
# API for delivering files from stacks
55
class FileController < ApplicationController
6+
include ActionController::Live
7+
68
rescue_from ActionController::MissingFile do
79
render plain: 'File not found', status: :not_found
810
end
@@ -24,7 +26,16 @@ def show
2426
ip: request.remote_ip
2527
)
2628

27-
send_file current_file.path, filename: current_file.file_name, disposition:
29+
response.headers['Content-Length'] = current_file.content_length
30+
send_stream(
31+
filename: current_file.file_name, # Sets the filename for the download
32+
type: current_file.content_type, # Sets the content type
33+
disposition:
34+
) do |stream|
35+
current_file.s3_object do |chunk|
36+
stream.write(chunk)
37+
end
38+
end
2839
end
2940
# rubocop:enable Metrics/AbcSize
3041

app/models/s3_client_factory.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# frozen_string_literal: true
2+
3+
# Creates an S3 client instance.
4+
class S3ClientFactory
5+
def self.create_client
6+
Aws::S3::Client.new(
7+
region: 'us-east-1',
8+
endpoint: Settings.s3.endpoint,
9+
force_path_style: true, # Often required for custom endpoints
10+
access_key_id: Settings.s3.access_key_id,
11+
secret_access_key: Settings.s3.secret_access_key
12+
)
13+
end
14+
end

app/models/stacks_file.rb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ def readable?
2727
path && File.world_readable?(path)
2828
end
2929

30+
def s3_object(&)
31+
S3ClientFactory.create_client.get_object(bucket: Settings.s3.bucket, key: storage_root.relative_path, &)
32+
rescue Aws::S3::Errors::NoSuchKey
33+
raise "Unable to find file at #{path}"
34+
end
35+
3036
def mtime
3137
@mtime ||= File.mtime(path) if readable?
3238
end
@@ -36,7 +42,15 @@ def etag
3642
end
3743

3844
def content_length
39-
@content_length ||= File.size(path) if readable?
45+
cocina_file['size']
46+
end
47+
48+
def content_type
49+
cocina_file['hasMimeType']
50+
end
51+
52+
def cocina_file
53+
@cocina_file ||= cocina.find_file(file_name)
4054
end
4155

4256
def path

compose.yaml

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
1-
version: '3.6'
2-
31
services:
42
web:
53
build: .
64
ports:
75
- 3001:3000
86
environment:
9-
SECRET_KEY_BASE: 'e55fb68eb0f1f4f82fbb56eafeebf1c134518fd466d1c3d01b2bfd3d6b5cd9347b4c15eecb3bdc0e4c5bc226dba626a23bb7b9ddb14f709638571f910002f639'
10-
RAILS_LOG_TO_STDOUT: 'true'
7+
SECRET_KEY_BASE: "e55fb68eb0f1f4f82fbb56eafeebf1c134518fd466d1c3d01b2bfd3d6b5cd9347b4c15eecb3bdc0e4c5bc226dba626a23bb7b9ddb14f709638571f910002f639"
8+
RAILS_LOG_TO_STDOUT: "true"
119
REMOTE_USER: blalbrit@stanford.edu
12-
SETTINGS__CORS__ALLOW_ORIGIN_URL: 'http://localhost:3000'
13-
tty: true
10+
SETTINGS__CORS__ALLOW_ORIGIN_URL: "http://localhost:3000"
11+
tty: true
12+
minio:
13+
image: minio/minio
14+
environment:
15+
MINIO_ROOT_USER: minio-user
16+
MINIO_ROOT_PASSWORD: minio-password
17+
MINIO_UPDATE: off
18+
command: server /data --json --console-address ':9090'
19+
ports: ["9000:9000/tcp", "9090:9090/tcp"] # open http://127.0.0.1:9090 (9000 is the API port)
20+
volumes:
21+
- minio_data:/data
22+
volumes:
23+
minio_data:

config/settings.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ features:
44
stacks:
55
storage_root: /stacks
66

7+
s3:
8+
access_key_id: ~
9+
secret_access_key: ~
10+
bucket: stacks-test
11+
endpoint: https://sul-weka-s3.stanford.edu
12+
713
imageserver:
814
base_uri: "http://imageserver-prod.stanford.edu/iiif/2/"
915

config/settings/test.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,9 @@ metrics_api_url: "https://example.com"
1515

1616
stacks:
1717
storage_root: spec/fixtures
18+
19+
s3:
20+
access_key_id: minio-user
21+
secret_access_key: minio-password
22+
bucket: stacks-test
23+
endpoint: http://localhost:9000

spec/factories/cocina.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def self.cocina_with_file(id: "druid:bb000cr7262", file_name: 'image.jp2', acces
2222
{ 'type' => 'md5', 'digest' => '8ff299eda08d7c506273840d52a03bf3' }
2323
],
2424
'hasMimeType' => mime_type,
25+
'size' => 12_345,
2526
'access' => file_access
2627
}
2728
]

0 commit comments

Comments
 (0)