api-get-object-file.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. /*
  2. * Minio Go Library for Amazon S3 Compatible Cloud Storage
  3. * Copyright 2015-2017 Minio, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package minio
  18. import (
  19. "io"
  20. "os"
  21. "path/filepath"
  22. "github.com/minio/minio-go/pkg/encrypt"
  23. "context"
  24. "github.com/minio/minio-go/pkg/s3utils"
  25. )
  26. // FGetObjectWithContext - download contents of an object to a local file.
  27. // The options can be used to specify the GET request further.
  28. func (c Client) FGetObjectWithContext(ctx context.Context, bucketName, objectName, filePath string, opts GetObjectOptions) error {
  29. return c.fGetObjectWithContext(ctx, bucketName, objectName, filePath, opts)
  30. }
  31. // FGetObject - download contents of an object to a local file.
  32. func (c Client) FGetObject(bucketName, objectName, filePath string, opts GetObjectOptions) error {
  33. return c.fGetObjectWithContext(context.Background(), bucketName, objectName, filePath, opts)
  34. }
  35. // FGetEncryptedObject - Decrypt and store an object at filePath.
  36. func (c Client) FGetEncryptedObject(bucketName, objectName, filePath string, materials encrypt.Materials) error {
  37. if materials == nil {
  38. return ErrInvalidArgument("Unable to recognize empty encryption properties")
  39. }
  40. return c.FGetObject(bucketName, objectName, filePath, GetObjectOptions{Materials: materials})
  41. }
  42. // fGetObjectWithContext - fgetObject wrapper function with context
  43. func (c Client) fGetObjectWithContext(ctx context.Context, bucketName, objectName, filePath string, opts GetObjectOptions) error {
  44. // Input validation.
  45. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  46. return err
  47. }
  48. if err := s3utils.CheckValidObjectName(objectName); err != nil {
  49. return err
  50. }
  51. // Verify if destination already exists.
  52. st, err := os.Stat(filePath)
  53. if err == nil {
  54. // If the destination exists and is a directory.
  55. if st.IsDir() {
  56. return ErrInvalidArgument("fileName is a directory.")
  57. }
  58. }
  59. // Proceed if file does not exist. return for all other errors.
  60. if err != nil {
  61. if !os.IsNotExist(err) {
  62. return err
  63. }
  64. }
  65. // Extract top level directory.
  66. objectDir, _ := filepath.Split(filePath)
  67. if objectDir != "" {
  68. // Create any missing top level directories.
  69. if err := os.MkdirAll(objectDir, 0700); err != nil {
  70. return err
  71. }
  72. }
  73. // Gather md5sum.
  74. objectStat, err := c.StatObject(bucketName, objectName, StatObjectOptions{opts})
  75. if err != nil {
  76. return err
  77. }
  78. // Write to a temporary file "fileName.part.minio" before saving.
  79. filePartPath := filePath + objectStat.ETag + ".part.minio"
  80. // If exists, open in append mode. If not create it as a part file.
  81. filePart, err := os.OpenFile(filePartPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
  82. if err != nil {
  83. return err
  84. }
  85. // Issue Stat to get the current offset.
  86. st, err = filePart.Stat()
  87. if err != nil {
  88. return err
  89. }
  90. // Initialize get object request headers to set the
  91. // appropriate range offsets to read from.
  92. if st.Size() > 0 {
  93. opts.SetRange(st.Size(), 0)
  94. }
  95. // Seek to current position for incoming reader.
  96. objectReader, objectStat, err := c.getObject(ctx, bucketName, objectName, opts)
  97. if err != nil {
  98. return err
  99. }
  100. // Write to the part file.
  101. if _, err = io.CopyN(filePart, objectReader, objectStat.Size); err != nil {
  102. return err
  103. }
  104. // Close the file before rename, this is specifically needed for Windows users.
  105. if err = filePart.Close(); err != nil {
  106. return err
  107. }
  108. // Safely completed. Now commit by renaming to actual filename.
  109. if err = os.Rename(filePartPath, filePath); err != nil {
  110. return err
  111. }
  112. // Return.
  113. return nil
  114. }