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  }