package
0.6.8
Repository: https://github.com/theopenlane/core.git
Documentation: pkg.go.dev

# README

Objects

Objects is a package designed to simplify the process of managing receipt of multipart/form-data requests and subsequently the uploading of files.

Usage

Assuming you have a HTML form like this:

<form action="/" method="post" enctype="multipart/form-data">
  <input type="file" name="form-field-1" />
  <input type="file" name="form-field-2" />
</form>

To create a new objects instance, you can do something like this:

objectManager, _ := objects.New(
 objects.WithMaxFileSize(10<<20),
 objects.WithKeys([]string{"form-field-1", "form-field-2"}),
 objects.WithUploaderFunc(
   func(ctx context.Context, u *Objects, files []FileUpload) ([]File, error) {
    // add your own custom uploader functionality here
    // or leave out to use the default uploader func
   }
 ),
 objects.WithValidationFunc(
  objects.ChainValidators(objects.MimeTypeValidator("image/jpeg", "image/png"),
   func(f objects.File) error {
    // Your own custom validation function on the file here
    return nil
   })),
 objects.WithStorage(s3Store),
)

The objectManager can be used with the provided middleware FileUploadMiddleware(objectManager) and added to the chain of middleware within the server. This is a generic http.Handler so it can be used with your router of choice. For example, to be used with echo:

echo.WrapMiddleware(objects.FileUploadMiddleware(objectManager))

Standard HTTP router

package main

import (
	"fmt"
	"net/http"

	"github.com/aws/aws-sdk-go-v2/aws"
	awsCreds "github.com/aws/aws-sdk-go-v2/credentials"

	"github.com/theopenlane/core/pkg/objects"
	"github.com/theopenlane/core/pkg/objects/storage"
)

func main() {
	s3Store, err := storage.NewS3FromConfig(aws.Config{
		Region: "us-west-2",
		Credentials: awsCreds.NewStaticCredentialsProvider(
			"accessKey", "secretKey", ""),
	}, storage.S3Options{
		Bucket: "std-router",
	})
	if err != nil {
		panic(err.Error())
	}

	objectsHandler, err := objects.New(
		objects.WithMaxFileSize(10<<20),
		objects.WithStorage(s3Store),
		objects.WithKeys([]string{"name", "mitb"}),
	)

	mux := http.NewServeMux()

	// upload all files with the "name" and "mitb" fields on this route
	mux.Handle("/", objects.FileUploadMiddleware(objectsHandler)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Uploading file")

		ctx := r.Context()

		// return all uploaded files
		f, err := objects.FilesFromContext(ctx)
		if err != nil {
			fmt.Println(err)
			return
		}

		// return uploaded files with the form field "mitb"
		ff, err := objects.FilesFromContextWithKey(ctx, "mitb")
		if err != nil {
			fmt.Println(err)
			return
		}

		fmt.Printf("%+v", ff)

		for _, v := range f {
			fmt.Printf("%+v", v)
			fmt.Println()
		}
	})))

	http.ListenAndServe(":3300", mux)
}

Ignoring non existent keys in the multipart Request

Sometimes, the keys you have configured the middleware might get dropped from the frontend for some reason, ideally the middleware fails if it cannot find a configured key in the request. To disable this behavior and ignore the missing key, you can make use of the WithIgnoreNonExistentKey(true) option to prevent the middleware from causing an error when such keys do not exists

Customizing the error response

Since Objects could be used as a middleware, it returns an error to the client if found, this might not match your existing structure, so to configure the response, use the WithErrorResponseHandler. The default is shown below and can be used as a template to define yours.

var errHandler objects.ErrResponseHandler = func(err error, statusCode int) http.HandlerFunc {
  return func(w http.ResponseWriter, _ *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(statusCode)
    fmt.Fprintf(w, `{"message" : "could not upload file", "error" : "%s"}`, err.Error())
  }
}

// add the following to the objects options:
// objects.WithErrorResponseHandler(errHandler)

Writing your own custom upload logic

The uploader function by default will just upload the file to the storage backend. In some cases you may want custom logic, e.g. update your local database with information. A custom UploaderFunc can be used to do this.

var uploader objects.UploaderFunc = func(ctx context.Context, u *objects.Objects, files []objects.FileUpload) ([]objects.File, error) {
	uploadedFiles := make([]objects.File, 0, len(files))

	for _, f := range files {
		// do things
	}

	return uploadedFiles, nil
}

// add the following to the objects options:
// objects.WithUploaderFunc(uploader)

Writing your custom validator logic

Sometimes, you could have some custom logic to validate uploads, in this example below, we limit the size of the upload based on the mimeypes of the uploaded files

var customValidator objects.ValidationFunc = func(f objects.File) error {
 switch f.MimeType {
 case "image/png":
  if f.Size > 4096 {
   return errors.New("file size too large")
  }

  return nil

 case "application/pdf":
  if f.Size > (1024 * 10) {
   return errors.New("file size too large")
  }

  return nil
 default:
  return nil
 }
}

// add the following to the objects options:
// objects.WithValidationFunc(customValidator)

# Packages

No description provided by the author
Package storage provides basic storage interfaces for storage providers to write / read objects to and from.

# Functions

ChainValidators returns a validator that accepts multiple validating criteria.
CreateURI creates a URI for the file based on the scheme, destination and key.
DetectContentType leverages http.DetectContentType to identify the content type of the provided data.
FilesFromContext returns all files that have been uploaded during the request.
FilesFromContextWithKey returns all files that have been uploaded during the request and sorts by the provided form field.
FileUploadMiddleware is a middleware that handles the file upload process this can be added to the middleware chain to handle file uploads prior to the main handler Since gqlgen handles file uploads differently, this middleware is not used in the graphql handler.
FormatFileSize converts a file size in bytes to a human-readable string in MB/GB notation.
GetFileIDsFromContext returns the file IDs from the context that are associated with the request.
MimeTypeValidator makes sure we only accept a valid mimetype.
New creates a new instance of Objects.
NewUploadFile function reads the content of the provided file path and returns a FileUpload object.
ReaderToSeeker function takes an io.Reader as input and returns an io.ReadSeeker which can be used to upload files to the object storage.
RemoveFileFromContext removes the file from the context based on the file ID.
StreamToByte function reads the content of the provided io.Reader and returns it as a byte slice.
UpdateFileInContextByKey updates the file in the context based on the key and the file ID.
UploadMetadata allows you to provide metadata for the upload.
UploadProgress allows you to provide a progress bar for the upload.
UploadProgressFinishMessage allows you to provide a message to display when the upload is complete.
UploadProgressOutput allows you to provide a writer for the progress bar.
WithDownloadFileOptions allows you to provide options for downloading a file.
WithErrorResponseHandler allows you to provide a custom error response handler.
WithIgnoreNonExistentKey allows you to configure the handler to skip multipart form key values which do not match the configured.
WithKeys allows you to configure the keys to look for in the multipart form on the REST request.
WithMaxFileSize allows you limit the size of file uploads to accept.
WithMaxMemory allows you limit the amount of memory to use when parsing a multipart form.
No description provided by the author
WithNameFuncGenerator allows you configure how you'd like to rename your uploaded files.
WithSkipper allows you to provide a custom skipper function.
WithStorage allows you to provide a storage backend to the Objects.
WithUploaderFunc allows you to provide a custom uploader function.
WithUploadFileOptions allows you to provide options for uploading a file.
WithValidationFunc allows you to provide a custom validation function.
WriteFilesToContext retrieves any existing files from the context, appends the new files to the existing files map based on the form field name, then returns a new context with the updated files map stored in it.

# Constants

No description provided by the author

# Variables

ErrFileOpenFailed is returned when a file could not be opened.
ErrFilesNotFound is returned when files could not be found in key from http request.
ErrInvalidMimeType is returned when a file has an invalid mime type.
ErrMustProvideStorageBackend is returned when a storage backend is not provided.
ErrSeekError is returned when an error occurs while seeking.
ErrUnexpectedType is returned when an invalid type is provided.
ErrUnsupportedMimeType is returned when a file has an unsupported mime type.
ErrValidationFailed is returned when a validation fails.
FileContextKey is the context key for the files This is the key that is used to store the files in the context, which is then used to retrieve the files in subsequent parts of the request this is different than the `key` in the multipart form, which is the form field name that the file was uploaded with.
No description provided by the author

# Structs

No description provided by the author
ContextKey is the key name for the additional context.
DownloadFileMetadata is a struct that holds information about a file that was successfully downloaded.
DownloadFileOptions is a struct that holds the options for downloading a file.
File is a struct that holds information about a file - there is no distinction between a File received in a multipart form request or used in a download.
FileUpload is the object that holds the file information.
Objects is the definition for handling objects and file uploads.
ParentObject is a struct that holds information about the parent object of a file.
Progress is used to track the progress of a file upload It implements the io.Writer interface so it can be passed to an io.TeeReader().
UploadedFileMetadata is a struct that holds information about a file that was successfully uploaded.
UploadFileOptions is a struct that holds the options for uploading a file.

# Interfaces

Storage is the primary interface that must be implemented by any storage backend and for interacting with Objects.

# Type aliases

ErrResponseHandler is a custom error that should be used to handle errors when an upload fails.
Files is a map of field names to a slice of files.
NameGeneratorFunc allows you alter the name of the file before it is ultimately uploaded and stored.
Option is a function that configures the Objects.
SKipperFunc is a function that defines whether to skip the middleware.
UploaderFunc is a function that handles the file upload process and returns the files uploaded.
UploadOption is a function that configures the UploadFileOptions.
ValidationFunc is a type that can be used to dynamically validate a file.