github.com/minio/minio-go/v6@v6.0.57/api-get-object-file.go (about) 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 18 package minio 19 20 import ( 21 "context" 22 "io" 23 "os" 24 "path/filepath" 25 26 "github.com/minio/minio-go/v6/pkg/s3utils" 27 ) 28 29 // FGetObjectWithContext - download contents of an object to a local file. 30 // The options can be used to specify the GET request further. 31 func (c Client) FGetObjectWithContext(ctx context.Context, bucketName, objectName, filePath string, opts GetObjectOptions) error { 32 return c.fGetObjectWithContext(ctx, bucketName, objectName, filePath, opts) 33 } 34 35 // FGetObject - download contents of an object to a local file. 36 func (c Client) FGetObject(bucketName, objectName, filePath string, opts GetObjectOptions) error { 37 return c.fGetObjectWithContext(context.Background(), bucketName, objectName, filePath, opts) 38 } 39 40 // fGetObjectWithContext - fgetObject wrapper function with context 41 func (c Client) fGetObjectWithContext(ctx context.Context, bucketName, objectName, filePath string, opts GetObjectOptions) error { 42 // Input validation. 43 if err := s3utils.CheckValidBucketName(bucketName); err != nil { 44 return err 45 } 46 if err := s3utils.CheckValidObjectName(objectName); err != nil { 47 return err 48 } 49 50 // Verify if destination already exists. 51 st, err := os.Stat(filePath) 52 if err == nil { 53 // If the destination exists and is a directory. 54 if st.IsDir() { 55 return ErrInvalidArgument("fileName is a directory.") 56 } 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 66 // Extract top level directory. 67 objectDir, _ := filepath.Split(filePath) 68 if objectDir != "" { 69 // Create any missing top level directories. 70 if err := os.MkdirAll(objectDir, 0700); err != nil { 71 return err 72 } 73 } 74 75 // Gather md5sum. 76 objectStat, err := c.StatObject(bucketName, objectName, StatObjectOptions{opts}) 77 if err != nil { 78 return err 79 } 80 81 // Write to a temporary file "fileName.part.minio" before saving. 82 filePartPath := filePath + objectStat.ETag + ".part.minio" 83 84 // If exists, open in append mode. If not create it as a part file. 85 filePart, err := os.OpenFile(filePartPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) 86 if err != nil { 87 return err 88 } 89 90 // If we return early with an error, be sure to close and delete 91 // filePart. If we have an error along the way there is a chance 92 // that filePart is somehow damaged, and we should discard it. 93 closeAndRemove := true 94 defer func() { 95 if closeAndRemove { 96 _ = filePart.Close() 97 _ = os.Remove(filePartPath) 98 } 99 }() 100 101 // Issue Stat to get the current offset. 102 st, err = filePart.Stat() 103 if err != nil { 104 return err 105 } 106 107 // Initialize get object request headers to set the 108 // appropriate range offsets to read from. 109 if st.Size() > 0 { 110 opts.SetRange(st.Size(), 0) 111 } 112 113 // Seek to current position for incoming reader. 114 objectReader, objectStat, _, err := c.getObject(ctx, bucketName, objectName, opts) 115 if err != nil { 116 return err 117 } 118 119 // Write to the part file. 120 if _, err = io.CopyN(filePart, objectReader, objectStat.Size); err != nil { 121 return err 122 } 123 124 // Close the file before rename, this is specifically needed for Windows users. 125 closeAndRemove = false 126 if err = filePart.Close(); err != nil { 127 return err 128 } 129 130 // Safely completed. Now commit by renaming to actual filename. 131 if err = os.Rename(filePartPath, filePath); err != nil { 132 return err 133 } 134 135 // Return. 136 return nil 137 }