1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
import options
import js/javascript
import types/mime
import utils/twtstr
type
DeallocFun = proc(buffer: pointer) {.noconv.}
Blob* = ref object of RootObj
isfile*: bool
size* {.jsget.}: uint64
ctype* {.jsget: "type".}: string
buffer*: pointer
deallocFun*: DeallocFun
WebFile* = ref object of Blob
webkitRelativePath {.jsget.}: string
path*: string
file: File #TODO maybe use fd?
proc newBlob*(buffer: pointer, size: int, ctype: string,
deallocFun: DeallocFun): Blob =
return Blob(
buffer: buffer,
size: uint64(size),
ctype: ctype,
deallocFun: deallocFun
)
proc finalize(blob: Blob) {.jsfin.} =
if blob.deallocFun != nil and blob.buffer != nil:
blob.deallocFun(blob.buffer)
proc newWebFile*(path: string, webkitRelativePath = ""): WebFile =
var file: File
if not open(file, path, fmRead):
raise newException(IOError, "Failed to open file")
return WebFile(
isfile: true,
path: path,
file: file,
ctype: guessContentType(path),
webkitRelativePath: webkitRelativePath
)
proc newWebFile(ctx: JSContext, fileBits: seq[string], fileName: string,
options = none(JSValue)): WebFile {.jsctor.} =
let file = WebFile(
isfile: false,
path: fileName,
deallocFun: dealloc
)
var len = 0
for blobPart in fileBits:
len += blobPart.len
file.buffer = alloc(len)
var buf = cast[ptr UncheckedArray[uint8]](file.buffer)
var i = 0
for blobPart in fileBits:
if blobPart.len > 0:
copyMem(addr buf[i], unsafeAddr blobPart[0], blobPart.len)
i += blobPart.len
file.size = uint64(len)
if options.isSome:
block ctype:
let t = fromJS[string](ctx, JS_GetPropertyStr(ctx, options.get, "type"))
if t.isNone:
break ctype
for c in t.get:
if c notin char(0x20)..char(0x7E):
break ctype
file.ctype &= c.tolower()
#TODO lastmodified
return file
#TODO File, Blob constructors
func size*(this: WebFile): uint64 {.jsfget.} =
#TODO use stat instead
if this.isfile:
return uint64(this.file.getFileSize())
return this.size
func name*(this: WebFile): string {.jsfget.} =
if this.path.len > 0 and this.path[^1] != '/':
return this.path.afterLast('/')
return this.path.afterLast('/', 2)
#TODO lastModified
proc addBlobModule*(ctx: JSContext) =
let blobCID = ctx.registerType(Blob)
ctx.registerType(WebFile, parent = blobCID, name = "File")
|