1
- use super :: { Blob , FileRange } ;
1
+ use super :: { Blob , FileRange , StreamingBlob } ;
2
2
use crate :: { InstanceMetrics , db:: Pool , error:: Result } ;
3
3
use chrono:: { DateTime , Utc } ;
4
4
use futures_util:: stream:: { Stream , TryStreamExt } ;
5
5
use sqlx:: Acquire ;
6
- use std:: sync:: Arc ;
6
+ use std:: { io , sync:: Arc } ;
7
7
8
8
pub ( crate ) struct DatabaseBackend {
9
9
pool : Pool ,
@@ -58,38 +58,27 @@ impl DatabaseBackend {
58
58
}
59
59
}
60
60
61
- pub ( super ) async fn get (
61
+ pub ( super ) async fn get_stream (
62
62
& self ,
63
63
path : & str ,
64
- max_size : usize ,
65
64
range : Option < FileRange > ,
66
- ) -> Result < Blob > {
67
- // The maximum size for a BYTEA (the type used for `content`) is 1GB, so this cast is safe:
68
- // https://www.postgresql.org/message-id/162867790712200946i7ba8eb92v908ac595c0c35aee%40mail.gmail.com
69
- let max_size = max_size. min ( i32:: MAX as usize ) as i32 ;
70
-
65
+ ) -> Result < StreamingBlob > {
71
66
struct Result {
72
67
path : String ,
73
68
mime : String ,
74
69
date_updated : DateTime < Utc > ,
75
70
compression : Option < i32 > ,
76
71
content : Option < Vec < u8 > > ,
77
- is_too_big : bool ,
78
72
}
79
73
80
74
let result = if let Some ( r) = range {
81
- // when we only want to get a range we can validate already if the range is small enough
82
- if ( r. end ( ) - r. start ( ) + 1 ) > max_size as u64 {
83
- return Err ( std:: io:: Error :: other ( crate :: error:: SizeLimitReached ) . into ( ) ) ;
84
- }
85
75
let range_start = i32:: try_from ( * r. start ( ) ) ?;
86
76
87
77
sqlx:: query_as!(
88
78
Result ,
89
79
r#"SELECT
90
80
path, mime, date_updated, compression,
91
- substring(content from $2 for $3) as content,
92
- FALSE as "is_too_big!"
81
+ substring(content from $2 for $3) as content
93
82
FROM files
94
83
WHERE path = $1;"# ,
95
84
path,
@@ -105,35 +94,35 @@ impl DatabaseBackend {
105
94
sqlx:: query_as!(
106
95
Result ,
107
96
r#"SELECT
108
- path, mime, date_updated, compression,
109
- (CASE WHEN LENGTH(content) <= $2 THEN content ELSE NULL END) AS content,
110
- (LENGTH(content) > $2) AS "is_too_big!"
97
+ path,
98
+ mime,
99
+ date_updated,
100
+ compression,
101
+ content
111
102
FROM files
112
103
WHERE path = $1;"# ,
113
104
path,
114
- max_size,
115
105
)
116
106
. fetch_optional ( & self . pool )
117
107
. await ?
118
108
. ok_or ( super :: PathNotFoundError ) ?
119
109
} ;
120
110
121
- if result. is_too_big {
122
- return Err ( std:: io:: Error :: other ( crate :: error:: SizeLimitReached ) . into ( ) ) ;
123
- }
124
-
125
111
let compression = result. compression . map ( |i| {
126
112
i. try_into ( )
127
113
. expect ( "invalid compression algorithm stored in database" )
128
114
} ) ;
129
- Ok ( Blob {
115
+ let content = result. content . unwrap_or_default ( ) ;
116
+ let content_len = content. len ( ) ;
117
+ Ok ( StreamingBlob {
130
118
path : result. path ,
131
119
mime : result
132
120
. mime
133
121
. parse ( )
134
122
. unwrap_or ( mime:: APPLICATION_OCTET_STREAM ) ,
135
123
date_updated : result. date_updated ,
136
- content : result. content . unwrap_or_default ( ) ,
124
+ content : Box :: new ( io:: Cursor :: new ( content) ) ,
125
+ content_length : content_len,
137
126
compression,
138
127
} )
139
128
}
0 commit comments