1
1
import type { R2Bucket , R2ListOptions } from '@cloudflare/workers-types/experimental'
2
+ import type { MultiPartData } from 'h3'
2
3
import mime from 'mime'
3
4
import { imageMeta } from 'image-meta'
4
5
import { defu } from 'defu'
6
+ import { randomUUID } from 'uncrypto'
5
7
6
8
const _buckets : Record < string , R2Bucket > = { }
7
9
@@ -24,48 +26,102 @@ export function useBucket (name: string = '') {
24
26
return _buckets [ bucketName ]
25
27
}
26
28
27
- export async function serveFiles ( bucket : R2Bucket , options : R2ListOptions = { } ) {
28
- const resolvedOptions = defu ( options , {
29
- limit : 500 ,
30
- include : [ 'httpMetadata' as const , 'customMetadata' as const ] ,
31
- } )
32
-
33
- // https://developers.cloudflare.com/r2/api/workers/workers-api-reference/#r2listoptions
34
- const listed = await bucket . list ( resolvedOptions )
35
- let truncated = listed . truncated
36
- let cursor = listed . truncated ? listed . cursor : undefined
37
-
38
- while ( truncated ) {
39
- const next = await bucket . list ( {
40
- ...options ,
41
- cursor : cursor ,
42
- } )
43
- listed . objects . push ( ...next . objects )
44
-
45
- truncated = next . truncated
46
- cursor = next . truncated ? next . cursor : undefined
47
- }
29
+ export function useBlob ( name : string = '' ) {
30
+ const isProxy = import . meta. dev && process . env . NUXT_HUB_URL
31
+
32
+ return {
33
+ async list ( options : R2ListOptions = { } ) {
34
+ if ( isProxy ) {
35
+ // TODO
36
+ } else {
37
+ const bucket = useBucket ( name )
38
+
39
+ const resolvedOptions = defu ( options , {
40
+ limit : 500 ,
41
+ include : [ 'httpMetadata' as const , 'customMetadata' as const ] ,
42
+ } )
43
+
44
+ // https://developers.cloudflare.com/r2/api/workers/workers-api-reference/#r2listoptions
45
+ const listed = await bucket . list ( resolvedOptions )
46
+ let truncated = listed . truncated
47
+ let cursor = listed . truncated ? listed . cursor : undefined
48
+
49
+ while ( truncated ) {
50
+ const next = await bucket . list ( {
51
+ ...options ,
52
+ cursor : cursor ,
53
+ } )
54
+ listed . objects . push ( ...next . objects )
55
+
56
+ truncated = next . truncated
57
+ cursor = next . truncated ? next . cursor : undefined
58
+ }
59
+
60
+ return listed . objects
61
+ }
62
+ } ,
63
+ async get ( key : string ) {
64
+ if ( isProxy ) {
65
+ // TODO
66
+ } else {
67
+ const bucket = useBucket ( name )
68
+ const object = await bucket . get ( key )
48
69
49
- return listed . objects
70
+ if ( ! object ) {
71
+ throw createError ( { message : 'File not found' , statusCode : 404 } )
72
+ }
73
+
74
+ setHeader ( useEvent ( ) , 'Content-Type' , object . httpMetadata ! . contentType ! )
75
+ setHeader ( useEvent ( ) , 'Content-Length' , object . size )
76
+
77
+ return object . body . getReader ( )
78
+ }
79
+ } ,
80
+ async put ( file : MultiPartData ) {
81
+ if ( isProxy ) {
82
+ // TODO
83
+ } else {
84
+ const bucket = useBucket ( name )
85
+
86
+ const type = file . type || getContentType ( file . filename )
87
+ const extension = getExtension ( type )
88
+ // TODO: ensure filename unicity
89
+ const filename = [ randomUUID ( ) , extension ] . filter ( Boolean ) . join ( '.' )
90
+ const httpMetadata = { contentType : type }
91
+ const customMetadata = getMetadata ( type , file . data )
92
+
93
+ return await bucket . put ( filename , toArrayBuffer ( file . data ) , { httpMetadata, customMetadata } )
94
+ }
95
+ } ,
96
+ async delete ( key : string ) {
97
+ if ( isProxy ) {
98
+ // TODO
99
+ } else {
100
+ const bucket = useBucket ( name )
101
+
102
+ return await bucket . delete ( key )
103
+ }
104
+ }
105
+ }
50
106
}
51
107
52
- export function getContentType ( pathOrExtension ?: string ) {
108
+ function getContentType ( pathOrExtension ?: string ) {
53
109
return ( pathOrExtension && mime . getType ( pathOrExtension ) ) || 'application/octet-stream'
54
110
}
55
111
56
- export function getExtension ( type ?: string ) {
112
+ function getExtension ( type ?: string ) {
57
113
return ( type && mime . getExtension ( type ) ) || ''
58
114
}
59
115
60
- export function getMetadata ( type : string , buffer : Buffer ) {
116
+ function getMetadata ( type : string , buffer : Buffer ) {
61
117
if ( type . startsWith ( 'image/' ) ) {
62
118
return imageMeta ( buffer ) as Record < string , any >
63
119
} else {
64
120
return { }
65
121
}
66
122
}
67
123
68
- export function toArrayBuffer ( buffer : Buffer ) {
124
+ function toArrayBuffer ( buffer : Buffer ) {
69
125
const arrayBuffer = new ArrayBuffer ( buffer . length )
70
126
const view = new Uint8Array ( arrayBuffer )
71
127
for ( let i = 0 ; i < buffer . length ; ++ i ) {
0 commit comments