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 18package minio 19 20import ( 21 "context" 22 "io" 23 "os" 24 "path/filepath" 25 26 "github.com/minio/minio-go/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. 31func (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. 36func (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 41func (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 // Issue Stat to get the current offset. 91 st, err = filePart.Stat() 92 if err != nil { 93 return err 94 } 95 96 // Initialize get object request headers to set the 97 // appropriate range offsets to read from. 98 if st.Size() > 0 { 99 opts.SetRange(st.Size(), 0) 100 } 101 102 // Seek to current position for incoming reader. 103 objectReader, objectStat, err := c.getObject(ctx, bucketName, objectName, opts) 104 if err != nil { 105 return err 106 } 107 108 // Write to the part file. 109 if _, err = io.CopyN(filePart, objectReader, objectStat.Size); err != nil { 110 return err 111 } 112 113 // Close the file before rename, this is specifically needed for Windows users. 114 if err = filePart.Close(); err != nil { 115 return err 116 } 117 118 // Safely completed. Now commit by renaming to actual filename. 119 if err = os.Rename(filePartPath, filePath); err != nil { 120 return err 121 } 122 123 // Return. 124 return nil 125} 126