File Storage

Introduction

The Goravel provides simple drivers for working with local filesystems, Amazon S3, Aliyun OSS, Tencent COS, Minio and Cloudinary. Even better, switching between these storage options between your local development machine and production server is amazingly simple as the API remains the same for each system. Goravel comes with a local driver, for other drivers, please check the corresponding independent extension package:

DriverLink
S3https://github.com/goravel/s3open in new window
OSShttps://github.com/goravel/ossopen in new window
COShttps://github.com/goravel/cosopen in new window
Miniohttps://github.com/goravel/minioopen in new window
Cloudinaryhttps://github.com/goravel/cloudinaryopen in new window

Configuration

Goravel's filesystem configuration file is located at config/filesystems.go. Within this file, you may configure all of your filesystem "disks", each disk represents a particular storage driver and storage location.

You may configure as many disks as you like and may even have multiple disks that use the same driver.

The Local Driver

When using the local driver, all file operations are relative to the root directory defined in your filesystems configuration file. By default, this value is set to the storage/app directory. Therefore, the following method would write to storage/app/example.txt:

facades.Storage().Put("example.txt", "Contents")

The Public Disk

The public`` disk included in your application's filesystemsconfiguration file is intended for files that are going to be publicly accessible. By default, thepublicdisk uses thelocaldriver and stores its files instorage/app/public`. If you want to visit these file from web, you can create a file routing:

facades.Route().Static("storage", "./storage/app/public")

Obtaining Disk Instances

The Storage facade may be used to interact with any of your configured disks. For example, you may use the Put method on the facade to store an avatar on the default disk. If you call methods on the Storage facade without first calling the Disk method, the method will automatically be passed to the default disk:

facades.Storage().Put("avatars/1.png", "Contents")

If your application interacts with multiple disks, you may use the Disk method on the Storage facade to work with files on a particular disk:

facades.Storage().Disk("s3").Put("avatars/1.png", "Contents")

Inject Context

facades.Storage().WithContext(ctx).Put("avatars/1.png", "Contents")

Retrieving Files

The Get method may be used to retrieve the contents of a file. The raw string contents of the file will be returned by the method. Remember, all file paths should be specified relative to the disk's root location:

contents := facades.Storage().Get("file.jpg")

The Exists method may be used to determine if a file exists on the disk:

if (facades.Storage().Disk("s3").Exists("file.jpg")) {
    // ...
}

The Missing method may be used to determine if a file is missing from the disk:

if (facades.Storage().Disk("s3").Missing("file.jpg")) {
    // ...
}

File URLs

You may use the Url method to get the URL for a given file. If you are using the local driver, this will typically just prepend /storage to the given path and return a relative URL to the file. If you are using the s3 driver, the fully qualified remote URL will be returned:

url := facades.Storage().Url("file.jpg")

When using the local driver, the return value of Url is not URL encoded. For this reason, we recommend always storing your files using names that will create valid URLs.

Temporary URLs

Using the TemporaryUrl method, you may create temporary URLs to files stored using the Non-local driver. This method accepts a path and a Time instance specifying when the URL should expire:

url, err := facades.Storage().TemporaryUrl(
    "file.jpg", time.Now().Add(5*time.Minute)
)

File Metadata

In addition to reading and writing files, Goravel can also provide information about the files themselves:

size := facades.Storage().Size("file.jpg")

The LastModified method returns the last modified time of the file:

time, err := facades.Storage().LastModified("file.jpg")

The MIME type of a given file may be obtained via the MimeType method:

mime, err := facades.Storage().MimeType("file.jpg")

Also can use the NewFile method:

import "github.com/goravel/framework/filesystem"

file, err := filesystem.NewFile("./logo.png")
size, err := file.Size()
lastModified, err := file.LastModified()
mime, err := file.MimeType()

File Paths

To obtain the path for a specific file, you can utilize the Path method. When using the local driver, this will provide you with the absolute path to the file. However, if you are using a driver like s3, the method will give you the file's relative path within the bucket:

path := facades.Storage().Path("file.jpg")

Storing Files

The Put method may be used to store file contents on a disk. Remember, all file paths should be specified relative to the "root" location configured for the disk:

err := facades.Storage().Put("file.jpg", contents)

You can also use PutFile and PutFileAs to save files directly on disk:

import "github.com/goravel/framework/filesystem"

// Automatically generate a unique ID for filename...
file, err := filesystem.NewFile("./logo.png")
path := facades.Storage().PutFile("photos", file)

// Manually specify a filename...
file, err := filesystem.NewFile("./logo.png")
path := facades.Storage().PutFileAs("photos", file, "photo.jpg")

There are a few important things to note about the PutFile method. Note that we only specified a directory name and not a filename. By default, the PutFile method will generate a unique ID to serve as the filename. The file's extension will be determined by examining the file's MIME type. The path to the file will be returned by the PutFile method so you can store the path, including the generated filename, in your database.

Copying & Moving Files

The Copy method may be used to copy an existing file to a new location on the disk, while the Move method may be used to rename or move an existing file to a new location:

err := facades.Storage().Copy("old/file.jpg", "new/file.jpg")

err := facades.Storage().Move("old/file.jpg", "new/file.jpg")

File Uploads

In web applications, one of the most common use cases for storing files is storing user-uploaded files such as photos and documents. Goravel makes it very easy to store uploaded files using the Store method on an uploaded file instance. Call the Store method with the path at which you wish to store the uploaded file:

func (r *UserController) Show(ctx http.Context) {
  file, err := ctx.Request().File("avatar")
  path, err := file.Store("avatars")
}

There are a few important things to note about this example. Note that we only specified a directory name, not a filename. By default, the Store method will generate a unique ID to serve as the filename. The file's extension will be determined by examining the file's MIME type. The path to the file will be returned by the Store method so you can store the path, including the generated filename, in your database.

You may also call the PutFile method on the Storage facade to perform the same file storage operation as the example above:

import "github.com/goravel/framework/filesystem"

file, err := filesystem.NewFile("./logo.png")
path := facades.Storage().PutFile("photos", file)

Specifying A File Name

If you do not want a filename to be automatically assigned to your stored file, you may use the StoreAs method, which receives the path, the filename, and the (optional) disk as its arguments:

file, err := ctx.Request().File("avatar")
path, err := file.StoreAs("avatars", "name")

You may also use the PutFileAs method on the Storage facade, which will perform the same file storage operation as the example above:

import "github.com/goravel/framework/filesystem"

file, err := filesystem.NewFile("./logo.png")
path := facades.Storage().PutFileAs("photos", file, "name")

If the file name specified by StoreAs and PutFileAs doesn't have a suffix, the suffix is automatically added based on the MIME of the file; otherwise, the specified file name is used directly.

Specifying A Disk

By default, this uploaded file's Store method will use your default disk. If you would like to specify another disk, please use the Disk method:

func (r *UserController) Show(ctx http.Context) {
  file, err := ctx.Request().File("avatar")
  path, err := file.Disk("s3").Store("avatars")
}

Other Uploaded File Information

If you would like to get the original name and extension of the uploaded file, you may do so using the GetClientOriginalName and GetClientOriginalExtension methods:

file, err := ctx.Request().File("avatar")

name := file.GetClientOriginalName()
extension := file.GetClientOriginalExtension()

However, keep in mind that the GetClientOriginalName and GetClientOriginalExtension methods are considered unsafe, as the file name and extension may be tampered with by a malicious user. For this reason, you should typically prefer the HashName and Extension methods to get a name and an extension for the given file upload:

file, err := ctx.Request().File("avatar")

name := file.HashName() // Generate a unique, random name...
extension, err := file.Extension() // Determine the file's extension based on the file's MIME type...

Deleting Files

The Delete method accepts a single filename or an array of files to delete:

err := facades.Storage().Delete("file.jpg")
err := facades.Storage().Delete("file.jpg", "file2.jpg")

If necessary, you may specify the disk that the file should be deleted from:

err := facades.Storage().Disk("s3").Delete("file.jpg")

Directories

Get All Files Within A Directory

The Files method returns a slice of all of the files in a given directory. If you would like to retrieve a list of all files within a given directory including all subdirectories, you may use the AllFiles method:

files, err := facades.Storage().Disk("s3").Files("directory")
files, err := facades.Storage().Disk("s3").AllFiles("directory")

Get All Directories Within A Directory

The Directories method returns a slice of all the directories within a given directory. Additionally, you may use the AllDirectories method to get a list of all directories within a given directory and all of its subdirectories:

directories, err := facades.Storage().Disk("s3").Directories("directory")
directories, err := facades.Storage().Disk("s3").AllDirectories("directory")

Create A Directory

The MakeDirectory method will create the given directory, including any needed subdirectories:

err := facades.Storage().MakeDirectory(directory)

Delete A Directory

Finally, the DeleteDirectory method may be used to remove a directory and all of its files:

err := facades.Storage().DeleteDirectory(directory)

Custom Filesystems

You can set the custom driver in the config/filesystems.go file.

"custom": map[string]any{
  "driver": "custom",
  "via":    filesystems.NewLocal(),
},

You need to implement the github.com/goravel/framework/contracts/filesystem/Driver interface in the via configuration item.

type Driver interface {
  AllDirectories(path string) ([]string, error)
  AllFiles(path string) ([]string, error)
  Copy(oldFile, newFile string) error
  Delete(file ...string) error
  DeleteDirectory(directory string) error
  Directories(path string) ([]string, error)
  Exists(file string) bool
  Files(path string) ([]string, error)
  Get(file string) (string, error)
  GetBytes(file string) ([]byte, error)
  LastModified(file string) (time.Time, error)
  MakeDirectory(directory string) error
  MimeType(file string) (string, error)
  Missing(file string) bool
  Move(oldFile, newFile string) error
  Path(file string) string
  Put(file, content string) error
  PutFile(path string, source File) (string, error)
  PutFileAs(path string, source File, name string) (string, error)
  Size(file string) (int64, error)
  TemporaryUrl(file string, time time.Time) (string, error)
  WithContext(ctx context.Context) Driver
  Url(file string) string
}

Note: Since the configuration has not been loaded when the custom driver is registered, so please use facades.Config().Env to obtain the configuration in the custom driver.