github.com/go-kivik/kivik/v4@v4.3.2/pouchdb/attachments.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 // use this file except in compliance with the License. You may obtain a copy of 3 // the License at 4 // 5 // http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 // License for the specific language governing permissions and limitations under 11 // the License. 12 13 //go:build js 14 15 package pouchdb 16 17 import ( 18 "context" 19 "io" 20 "strings" 21 "sync" 22 23 "github.com/gopherjs/gopherjs/js" 24 "github.com/gopherjs/jsbuiltin" 25 26 "github.com/go-kivik/kivik/v4/driver" 27 "github.com/go-kivik/kivik/v4/pouchdb/bindings" 28 ) 29 30 func (d *db) PutAttachment(ctx context.Context, docID string, att *driver.Attachment, options driver.Options) (newRev string, err error) { 31 opts := map[string]interface{}{} 32 options.Apply(opts) 33 rev, _ := opts["rev"].(string) 34 result, err := d.db.PutAttachment(ctx, docID, att.Filename, rev, att.Content, att.ContentType) 35 if err != nil { 36 return "", err 37 } 38 return result.Get("rev").String(), nil 39 } 40 41 func (d *db) GetAttachment(ctx context.Context, docID, filename string, options driver.Options) (*driver.Attachment, error) { 42 opts := map[string]interface{}{} 43 options.Apply(opts) 44 result, err := d.db.GetAttachment(ctx, docID, filename, opts) 45 if err != nil { 46 return nil, err 47 } 48 return parseAttachment(result) 49 } 50 51 func parseAttachment(obj *js.Object) (att *driver.Attachment, err error) { 52 defer bindings.RecoverError(&err) 53 if jsbuiltin.TypeOf(obj.Get("write")) == jsbuiltin.TypeFunction { 54 // This looks like a Buffer object; we're in Node.js 55 body := obj.Call("toString", "binary").String() 56 // It might make sense to wrap the Buffer itself in an io.Reader interface, 57 // but since this is only for testing, I'm taking the lazy way out, even 58 // though it means slurping an extra copy into memory. 59 return &driver.Attachment{ 60 Content: io.NopCloser(strings.NewReader(body)), 61 }, nil 62 } 63 // We're in the browser 64 return &driver.Attachment{ 65 ContentType: obj.Get("type").String(), 66 Content: &blobReader{Object: obj}, 67 }, nil 68 } 69 70 type blobReader struct { 71 *js.Object 72 offset int 73 Size int `js:"size"` 74 } 75 76 var _ io.ReadCloser = &blobReader{} 77 78 func (b *blobReader) Read(p []byte) (n int, err error) { 79 defer bindings.RecoverError(&err) 80 if b.offset >= b.Size { 81 return 0, io.EOF 82 } 83 end := b.offset + len(p) + 1 // end is the first byte not included, not the last byte included, so add 1 84 if end > b.Size { 85 end = b.Size 86 } 87 slice := b.Call("slice", b.offset, end) 88 fileReader := js.Global.Get("FileReader").New() 89 var wg sync.WaitGroup 90 wg.Add(1) 91 fileReader.Set("onload", js.MakeFunc(func(this *js.Object, _ []*js.Object) interface{} { 92 defer wg.Done() 93 n = copy(p, js.Global.Get("Uint8Array").New(this.Get("result")).Interface().([]uint8)) 94 return nil 95 })) 96 fileReader.Call("readAsArrayBuffer", slice) 97 wg.Wait() 98 b.offset += n 99 return 100 } 101 102 func (b *blobReader) Close() (err error) { 103 defer bindings.RecoverError(&err) 104 b.Call("close") 105 return nil 106 } 107 108 func (d *db) DeleteAttachment(ctx context.Context, docID, filename string, options driver.Options) (newRev string, err error) { 109 opts := map[string]interface{}{} 110 options.Apply(opts) 111 rev, _ := opts["rev"].(string) 112 result, err := d.db.RemoveAttachment(ctx, docID, filename, rev) 113 if err != nil { 114 return "", err 115 } 116 return result.Get("rev").String(), nil 117 }