github.com/lianghucheng/zrddz@v0.0.0-20200923083010-c71f680932e2/src/gopkg.in/mgo.v2/gridfs.go (about) 1 // mgo - MongoDB driver for Go 2 // 3 // Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net> 4 // 5 // All rights reserved. 6 // 7 // Redistribution and use in source and binary forms, with or without 8 // modification, are permitted provided that the following conditions are met: 9 // 10 // 1. Redistributions of source code must retain the above copyright notice, this 11 // list of conditions and the following disclaimer. 12 // 2. Redistributions in binary form must reproduce the above copyright notice, 13 // this list of conditions and the following disclaimer in the documentation 14 // and/or other materials provided with the distribution. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27 package mgo 28 29 import ( 30 "crypto/md5" 31 "encoding/hex" 32 "errors" 33 "hash" 34 "io" 35 "os" 36 "sync" 37 "time" 38 39 "gopkg.in/mgo.v2/bson" 40 ) 41 42 type GridFS struct { 43 Files *Collection 44 Chunks *Collection 45 } 46 47 type gfsFileMode int 48 49 const ( 50 gfsClosed gfsFileMode = 0 51 gfsReading gfsFileMode = 1 52 gfsWriting gfsFileMode = 2 53 ) 54 55 type GridFile struct { 56 m sync.Mutex 57 c sync.Cond 58 gfs *GridFS 59 mode gfsFileMode 60 err error 61 62 chunk int 63 offset int64 64 65 wpending int 66 wbuf []byte 67 wsum hash.Hash 68 69 rbuf []byte 70 rcache *gfsCachedChunk 71 72 doc gfsFile 73 } 74 75 type gfsFile struct { 76 Id interface{} "_id" 77 ChunkSize int "chunkSize" 78 UploadDate time.Time "uploadDate" 79 Length int64 ",minsize" 80 MD5 string 81 Filename string ",omitempty" 82 ContentType string "contentType,omitempty" 83 Metadata *bson.Raw ",omitempty" 84 } 85 86 type gfsChunk struct { 87 Id interface{} "_id" 88 FilesId interface{} "files_id" 89 N int 90 Data []byte 91 } 92 93 type gfsCachedChunk struct { 94 wait sync.Mutex 95 n int 96 data []byte 97 err error 98 } 99 100 func newGridFS(db *Database, prefix string) *GridFS { 101 return &GridFS{db.C(prefix + ".files"), db.C(prefix + ".chunks")} 102 } 103 104 func (gfs *GridFS) newFile() *GridFile { 105 file := &GridFile{gfs: gfs} 106 file.c.L = &file.m 107 //runtime.SetFinalizer(file, finalizeFile) 108 return file 109 } 110 111 func finalizeFile(file *GridFile) { 112 file.Close() 113 } 114 115 // Create creates a new file with the provided name in the GridFS. If the file 116 // name already exists, a new version will be inserted with an up-to-date 117 // uploadDate that will cause it to be atomically visible to the Open and 118 // OpenId methods. If the file name is not important, an empty name may be 119 // provided and the file Id used instead. 120 // 121 // It's important to Close files whether they are being written to 122 // or read from, and to check the err result to ensure the operation 123 // completed successfully. 124 // 125 // A simple example inserting a new file: 126 // 127 // func check(err error) { 128 // if err != nil { 129 // panic(err.String()) 130 // } 131 // } 132 // file, err := db.GridFS("fs").Create("myfile.txt") 133 // check(err) 134 // n, err := file.Write([]byte("Hello world!")) 135 // check(err) 136 // err = file.Close() 137 // check(err) 138 // fmt.Printf("%d bytes written\n", n) 139 // 140 // The io.Writer interface is implemented by *GridFile and may be used to 141 // help on the file creation. For example: 142 // 143 // file, err := db.GridFS("fs").Create("myfile.txt") 144 // check(err) 145 // messages, err := os.Open("/var/log/messages") 146 // check(err) 147 // defer messages.Close() 148 // err = io.Copy(file, messages) 149 // check(err) 150 // err = file.Close() 151 // check(err) 152 // 153 func (gfs *GridFS) Create(name string) (file *GridFile, err error) { 154 file = gfs.newFile() 155 file.mode = gfsWriting 156 file.wsum = md5.New() 157 file.doc = gfsFile{Id: bson.NewObjectId(), ChunkSize: 255 * 1024, Filename: name} 158 return 159 } 160 161 // OpenId returns the file with the provided id, for reading. 162 // If the file isn't found, err will be set to mgo.ErrNotFound. 163 // 164 // It's important to Close files whether they are being written to 165 // or read from, and to check the err result to ensure the operation 166 // completed successfully. 167 // 168 // The following example will print the first 8192 bytes from the file: 169 // 170 // func check(err error) { 171 // if err != nil { 172 // panic(err.String()) 173 // } 174 // } 175 // file, err := db.GridFS("fs").OpenId(objid) 176 // check(err) 177 // b := make([]byte, 8192) 178 // n, err := file.Read(b) 179 // check(err) 180 // fmt.Println(string(b)) 181 // check(err) 182 // err = file.Close() 183 // check(err) 184 // fmt.Printf("%d bytes read\n", n) 185 // 186 // The io.Reader interface is implemented by *GridFile and may be used to 187 // deal with it. As an example, the following snippet will dump the whole 188 // file into the standard output: 189 // 190 // file, err := db.GridFS("fs").OpenId(objid) 191 // check(err) 192 // err = io.Copy(os.Stdout, file) 193 // check(err) 194 // err = file.Close() 195 // check(err) 196 // 197 func (gfs *GridFS) OpenId(id interface{}) (file *GridFile, err error) { 198 var doc gfsFile 199 err = gfs.Files.Find(bson.M{"_id": id}).One(&doc) 200 if err != nil { 201 return 202 } 203 file = gfs.newFile() 204 file.mode = gfsReading 205 file.doc = doc 206 return 207 } 208 209 // Open returns the most recently uploaded file with the provided 210 // name, for reading. If the file isn't found, err will be set 211 // to mgo.ErrNotFound. 212 // 213 // It's important to Close files whether they are being written to 214 // or read from, and to check the err result to ensure the operation 215 // completed successfully. 216 // 217 // The following example will print the first 8192 bytes from the file: 218 // 219 // file, err := db.GridFS("fs").Open("myfile.txt") 220 // check(err) 221 // b := make([]byte, 8192) 222 // n, err := file.Read(b) 223 // check(err) 224 // fmt.Println(string(b)) 225 // check(err) 226 // err = file.Close() 227 // check(err) 228 // fmt.Printf("%d bytes read\n", n) 229 // 230 // The io.Reader interface is implemented by *GridFile and may be used to 231 // deal with it. As an example, the following snippet will dump the whole 232 // file into the standard output: 233 // 234 // file, err := db.GridFS("fs").Open("myfile.txt") 235 // check(err) 236 // err = io.Copy(os.Stdout, file) 237 // check(err) 238 // err = file.Close() 239 // check(err) 240 // 241 func (gfs *GridFS) Open(name string) (file *GridFile, err error) { 242 var doc gfsFile 243 err = gfs.Files.Find(bson.M{"filename": name}).Sort("-uploadDate").One(&doc) 244 if err != nil { 245 return 246 } 247 file = gfs.newFile() 248 file.mode = gfsReading 249 file.doc = doc 250 return 251 } 252 253 // OpenNext opens the next file from iter for reading, sets *file to it, 254 // and returns true on the success case. If no more documents are available 255 // on iter or an error occurred, *file is set to nil and the result is false. 256 // Errors will be available via iter.Err(). 257 // 258 // The iter parameter must be an iterator on the GridFS files collection. 259 // Using the GridFS.Find method is an easy way to obtain such an iterator, 260 // but any iterator on the collection will work. 261 // 262 // If the provided *file is non-nil, OpenNext will close it before attempting 263 // to iterate to the next element. This means that in a loop one only 264 // has to worry about closing files when breaking out of the loop early 265 // (break, return, or panic). 266 // 267 // For example: 268 // 269 // gfs := db.GridFS("fs") 270 // query := gfs.Find(nil).Sort("filename") 271 // iter := query.Iter() 272 // var f *mgo.GridFile 273 // for gfs.OpenNext(iter, &f) { 274 // fmt.Printf("Filename: %s\n", f.Name()) 275 // } 276 // if iter.Close() != nil { 277 // panic(iter.Close()) 278 // } 279 // 280 func (gfs *GridFS) OpenNext(iter *Iter, file **GridFile) bool { 281 if *file != nil { 282 // Ignoring the error here shouldn't be a big deal 283 // as we're reading the file and the loop iteration 284 // for this file is finished. 285 _ = (*file).Close() 286 } 287 var doc gfsFile 288 if !iter.Next(&doc) { 289 *file = nil 290 return false 291 } 292 f := gfs.newFile() 293 f.mode = gfsReading 294 f.doc = doc 295 *file = f 296 return true 297 } 298 299 // Find runs query on GridFS's files collection and returns 300 // the resulting Query. 301 // 302 // This logic: 303 // 304 // gfs := db.GridFS("fs") 305 // iter := gfs.Find(nil).Iter() 306 // 307 // Is equivalent to: 308 // 309 // files := db.C("fs" + ".files") 310 // iter := files.Find(nil).Iter() 311 // 312 func (gfs *GridFS) Find(query interface{}) *Query { 313 return gfs.Files.Find(query) 314 } 315 316 // RemoveId deletes the file with the provided id from the GridFS. 317 func (gfs *GridFS) RemoveId(id interface{}) error { 318 err := gfs.Files.Remove(bson.M{"_id": id}) 319 if err != nil { 320 return err 321 } 322 _, err = gfs.Chunks.RemoveAll(bson.D{{"files_id", id}}) 323 return err 324 } 325 326 type gfsDocId struct { 327 Id interface{} "_id" 328 } 329 330 // Remove deletes all files with the provided name from the GridFS. 331 func (gfs *GridFS) Remove(name string) (err error) { 332 iter := gfs.Files.Find(bson.M{"filename": name}).Select(bson.M{"_id": 1}).Iter() 333 var doc gfsDocId 334 for iter.Next(&doc) { 335 if e := gfs.RemoveId(doc.Id); e != nil { 336 err = e 337 } 338 } 339 if err == nil { 340 err = iter.Close() 341 } 342 return err 343 } 344 345 func (file *GridFile) assertMode(mode gfsFileMode) { 346 switch file.mode { 347 case mode: 348 return 349 case gfsWriting: 350 panic("GridFile is open for writing") 351 case gfsReading: 352 panic("GridFile is open for reading") 353 case gfsClosed: 354 panic("GridFile is closed") 355 default: 356 panic("internal error: missing GridFile mode") 357 } 358 } 359 360 // SetChunkSize sets size of saved chunks. Once the file is written to, it 361 // will be split in blocks of that size and each block saved into an 362 // independent chunk document. The default chunk size is 255kb. 363 // 364 // It is a runtime error to call this function once the file has started 365 // being written to. 366 func (file *GridFile) SetChunkSize(bytes int) { 367 file.assertMode(gfsWriting) 368 debugf("GridFile %p: setting chunk size to %d", file, bytes) 369 file.m.Lock() 370 file.doc.ChunkSize = bytes 371 file.m.Unlock() 372 } 373 374 // Id returns the current file Id. 375 func (file *GridFile) Id() interface{} { 376 return file.doc.Id 377 } 378 379 // SetId changes the current file Id. 380 // 381 // It is a runtime error to call this function once the file has started 382 // being written to, or when the file is not open for writing. 383 func (file *GridFile) SetId(id interface{}) { 384 file.assertMode(gfsWriting) 385 file.m.Lock() 386 file.doc.Id = id 387 file.m.Unlock() 388 } 389 390 // Name returns the optional file name. An empty string will be returned 391 // in case it is unset. 392 func (file *GridFile) Name() string { 393 return file.doc.Filename 394 } 395 396 // SetName changes the optional file name. An empty string may be used to 397 // unset it. 398 // 399 // It is a runtime error to call this function when the file is not open 400 // for writing. 401 func (file *GridFile) SetName(name string) { 402 file.assertMode(gfsWriting) 403 file.m.Lock() 404 file.doc.Filename = name 405 file.m.Unlock() 406 } 407 408 // ContentType returns the optional file content type. An empty string will be 409 // returned in case it is unset. 410 func (file *GridFile) ContentType() string { 411 return file.doc.ContentType 412 } 413 414 // ContentType changes the optional file content type. An empty string may be 415 // used to unset it. 416 // 417 // It is a runtime error to call this function when the file is not open 418 // for writing. 419 func (file *GridFile) SetContentType(ctype string) { 420 file.assertMode(gfsWriting) 421 file.m.Lock() 422 file.doc.ContentType = ctype 423 file.m.Unlock() 424 } 425 426 // GetMeta unmarshals the optional "metadata" field associated with the 427 // file into the result parameter. The meaning of keys under that field 428 // is user-defined. For example: 429 // 430 // result := struct{ INode int }{} 431 // err = file.GetMeta(&result) 432 // if err != nil { 433 // panic(err.String()) 434 // } 435 // fmt.Printf("inode: %d\n", result.INode) 436 // 437 func (file *GridFile) GetMeta(result interface{}) (err error) { 438 file.m.Lock() 439 if file.doc.Metadata != nil { 440 err = bson.Unmarshal(file.doc.Metadata.Data, result) 441 } 442 file.m.Unlock() 443 return 444 } 445 446 // SetMeta changes the optional "metadata" field associated with the 447 // file. The meaning of keys under that field is user-defined. 448 // For example: 449 // 450 // file.SetMeta(bson.M{"inode": inode}) 451 // 452 // It is a runtime error to call this function when the file is not open 453 // for writing. 454 func (file *GridFile) SetMeta(metadata interface{}) { 455 file.assertMode(gfsWriting) 456 data, err := bson.Marshal(metadata) 457 file.m.Lock() 458 if err != nil && file.err == nil { 459 file.err = err 460 } else { 461 file.doc.Metadata = &bson.Raw{Data: data} 462 } 463 file.m.Unlock() 464 } 465 466 // Size returns the file size in bytes. 467 func (file *GridFile) Size() (bytes int64) { 468 file.m.Lock() 469 bytes = file.doc.Length 470 file.m.Unlock() 471 return 472 } 473 474 // MD5 returns the file MD5 as a hex-encoded string. 475 func (file *GridFile) MD5() (md5 string) { 476 return file.doc.MD5 477 } 478 479 // UploadDate returns the file upload time. 480 func (file *GridFile) UploadDate() time.Time { 481 return file.doc.UploadDate 482 } 483 484 // SetUploadDate changes the file upload time. 485 // 486 // It is a runtime error to call this function when the file is not open 487 // for writing. 488 func (file *GridFile) SetUploadDate(t time.Time) { 489 file.assertMode(gfsWriting) 490 file.m.Lock() 491 file.doc.UploadDate = t 492 file.m.Unlock() 493 } 494 495 // Close flushes any pending changes in case the file is being written 496 // to, waits for any background operations to finish, and closes the file. 497 // 498 // It's important to Close files whether they are being written to 499 // or read from, and to check the err result to ensure the operation 500 // completed successfully. 501 func (file *GridFile) Close() (err error) { 502 file.m.Lock() 503 defer file.m.Unlock() 504 if file.mode == gfsWriting { 505 if len(file.wbuf) > 0 && file.err == nil { 506 file.insertChunk(file.wbuf) 507 file.wbuf = file.wbuf[0:0] 508 } 509 file.completeWrite() 510 } else if file.mode == gfsReading && file.rcache != nil { 511 file.rcache.wait.Lock() 512 file.rcache = nil 513 } 514 file.mode = gfsClosed 515 debugf("GridFile %p: closed", file) 516 return file.err 517 } 518 519 func (file *GridFile) completeWrite() { 520 for file.wpending > 0 { 521 debugf("GridFile %p: waiting for %d pending chunks to complete file write", file, file.wpending) 522 file.c.Wait() 523 } 524 if file.err == nil { 525 hexsum := hex.EncodeToString(file.wsum.Sum(nil)) 526 if file.doc.UploadDate.IsZero() { 527 file.doc.UploadDate = bson.Now() 528 } 529 file.doc.MD5 = hexsum 530 file.err = file.gfs.Files.Insert(file.doc) 531 } 532 if file.err != nil { 533 file.gfs.Chunks.RemoveAll(bson.D{{"files_id", file.doc.Id}}) 534 } 535 if file.err == nil { 536 index := Index{ 537 Key: []string{"files_id", "n"}, 538 Unique: true, 539 } 540 file.err = file.gfs.Chunks.EnsureIndex(index) 541 } 542 } 543 544 // Abort cancels an in-progress write, preventing the file from being 545 // automically created and ensuring previously written chunks are 546 // removed when the file is closed. 547 // 548 // It is a runtime error to call Abort when the file was not opened 549 // for writing. 550 func (file *GridFile) Abort() { 551 if file.mode != gfsWriting { 552 panic("file.Abort must be called on file opened for writing") 553 } 554 file.err = errors.New("write aborted") 555 } 556 557 // Write writes the provided data to the file and returns the 558 // number of bytes written and an error in case something 559 // wrong happened. 560 // 561 // The file will internally cache the data so that all but the last 562 // chunk sent to the database have the size defined by SetChunkSize. 563 // This also means that errors may be deferred until a future call 564 // to Write or Close. 565 // 566 // The parameters and behavior of this function turn the file 567 // into an io.Writer. 568 func (file *GridFile) Write(data []byte) (n int, err error) { 569 file.assertMode(gfsWriting) 570 file.m.Lock() 571 debugf("GridFile %p: writing %d bytes", file, len(data)) 572 defer file.m.Unlock() 573 574 if file.err != nil { 575 return 0, file.err 576 } 577 578 n = len(data) 579 file.doc.Length += int64(n) 580 chunkSize := file.doc.ChunkSize 581 582 if len(file.wbuf)+len(data) < chunkSize { 583 file.wbuf = append(file.wbuf, data...) 584 return 585 } 586 587 // First, flush file.wbuf complementing with data. 588 if len(file.wbuf) > 0 { 589 missing := chunkSize - len(file.wbuf) 590 if missing > len(data) { 591 missing = len(data) 592 } 593 file.wbuf = append(file.wbuf, data[:missing]...) 594 data = data[missing:] 595 file.insertChunk(file.wbuf) 596 file.wbuf = file.wbuf[0:0] 597 } 598 599 // Then, flush all chunks from data without copying. 600 for len(data) > chunkSize { 601 size := chunkSize 602 if size > len(data) { 603 size = len(data) 604 } 605 file.insertChunk(data[:size]) 606 data = data[size:] 607 } 608 609 // And append the rest for a future call. 610 file.wbuf = append(file.wbuf, data...) 611 612 return n, file.err 613 } 614 615 func (file *GridFile) insertChunk(data []byte) { 616 n := file.chunk 617 file.chunk++ 618 debugf("GridFile %p: adding to checksum: %q", file, string(data)) 619 file.wsum.Write(data) 620 621 for file.doc.ChunkSize*file.wpending >= 1024*1024 { 622 // Hold on.. we got a MB pending. 623 file.c.Wait() 624 if file.err != nil { 625 return 626 } 627 } 628 629 file.wpending++ 630 631 debugf("GridFile %p: inserting chunk %d with %d bytes", file, n, len(data)) 632 633 // We may not own the memory of data, so rather than 634 // simply copying it, we'll marshal the document ahead of time. 635 data, err := bson.Marshal(gfsChunk{bson.NewObjectId(), file.doc.Id, n, data}) 636 if err != nil { 637 file.err = err 638 return 639 } 640 641 go func() { 642 err := file.gfs.Chunks.Insert(bson.Raw{Data: data}) 643 file.m.Lock() 644 file.wpending-- 645 if err != nil && file.err == nil { 646 file.err = err 647 } 648 file.c.Broadcast() 649 file.m.Unlock() 650 }() 651 } 652 653 // Seek sets the offset for the next Read or Write on file to 654 // offset, interpreted according to whence: 0 means relative to 655 // the origin of the file, 1 means relative to the current offset, 656 // and 2 means relative to the end. It returns the new offset and 657 // an error, if any. 658 func (file *GridFile) Seek(offset int64, whence int) (pos int64, err error) { 659 file.m.Lock() 660 debugf("GridFile %p: seeking for %s (whence=%d)", file, offset, whence) 661 defer file.m.Unlock() 662 switch whence { 663 case os.SEEK_SET: 664 case os.SEEK_CUR: 665 offset += file.offset 666 case os.SEEK_END: 667 offset += file.doc.Length 668 default: 669 panic("unsupported whence value") 670 } 671 if offset > file.doc.Length { 672 return file.offset, errors.New("seek past end of file") 673 } 674 if offset == file.doc.Length { 675 // If we're seeking to the end of the file, 676 // no need to read anything. This enables 677 // a client to find the size of the file using only the 678 // io.ReadSeeker interface with low overhead. 679 file.offset = offset 680 return file.offset, nil 681 } 682 chunk := int(offset / int64(file.doc.ChunkSize)) 683 if chunk+1 == file.chunk && offset >= file.offset { 684 file.rbuf = file.rbuf[int(offset-file.offset):] 685 file.offset = offset 686 return file.offset, nil 687 } 688 file.offset = offset 689 file.chunk = chunk 690 file.rbuf = nil 691 file.rbuf, err = file.getChunk() 692 if err == nil { 693 file.rbuf = file.rbuf[int(file.offset-int64(chunk)*int64(file.doc.ChunkSize)):] 694 } 695 return file.offset, err 696 } 697 698 // Read reads into b the next available data from the file and 699 // returns the number of bytes written and an error in case 700 // something wrong happened. At the end of the file, n will 701 // be zero and err will be set to io.EOF. 702 // 703 // The parameters and behavior of this function turn the file 704 // into an io.Reader. 705 func (file *GridFile) Read(b []byte) (n int, err error) { 706 file.assertMode(gfsReading) 707 file.m.Lock() 708 debugf("GridFile %p: reading at offset %d into buffer of length %d", file, file.offset, len(b)) 709 defer file.m.Unlock() 710 if file.offset == file.doc.Length { 711 return 0, io.EOF 712 } 713 for err == nil { 714 i := copy(b, file.rbuf) 715 n += i 716 file.offset += int64(i) 717 file.rbuf = file.rbuf[i:] 718 if i == len(b) || file.offset == file.doc.Length { 719 break 720 } 721 b = b[i:] 722 file.rbuf, err = file.getChunk() 723 } 724 return n, err 725 } 726 727 func (file *GridFile) getChunk() (data []byte, err error) { 728 cache := file.rcache 729 file.rcache = nil 730 if cache != nil && cache.n == file.chunk { 731 debugf("GridFile %p: Getting chunk %d from cache", file, file.chunk) 732 cache.wait.Lock() 733 data, err = cache.data, cache.err 734 } else { 735 debugf("GridFile %p: Fetching chunk %d", file, file.chunk) 736 var doc gfsChunk 737 err = file.gfs.Chunks.Find(bson.D{{"files_id", file.doc.Id}, {"n", file.chunk}}).One(&doc) 738 data = doc.Data 739 } 740 file.chunk++ 741 if int64(file.chunk)*int64(file.doc.ChunkSize) < file.doc.Length { 742 // Read the next one in background. 743 cache = &gfsCachedChunk{n: file.chunk} 744 cache.wait.Lock() 745 debugf("GridFile %p: Scheduling chunk %d for background caching", file, file.chunk) 746 // Clone the session to avoid having it closed in between. 747 chunks := file.gfs.Chunks 748 session := chunks.Database.Session.Clone() 749 go func(id interface{}, n int) { 750 defer session.Close() 751 chunks = chunks.With(session) 752 var doc gfsChunk 753 cache.err = chunks.Find(bson.D{{"files_id", id}, {"n", n}}).One(&doc) 754 cache.data = doc.Data 755 cache.wait.Unlock() 756 }(file.doc.Id, file.chunk) 757 file.rcache = cache 758 } 759 debugf("Returning err: %#v", err) 760 return 761 }