From 10ada241fef03d7608375351e00aa85a4819c0cb Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sat, 21 Sep 2024 16:34:53 +0100 Subject: [PATCH] Update FAQ and restructure readme (#20) * update readme with faqs * fix markdown lint * add generic readertoseeker implmentation * fix seeker * add reader --- README.md | 24 +++++++++++++++++------- reader.go | 31 +++++++++++++++++++++++++++++++ storage/s3.go | 8 +++++++- 3 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 reader.go diff --git a/README.md b/README.md index ff198a5..b13ee62 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,18 @@ for your web apps. It follows the standard `http.Handler` and `http.HandlerFunc` interfaces so you can always use with any of framework or the standard library router. -Multiple files per form field are already supported - -> Name and idea was gotten from the insanely popular multer package -> in NodeJS that does the same. +> [!NOTE] +> Name and idea was gotten from the insanely popular multer package in NodeJS that does the same + +- [Installation](#installation) +- [Usage](#usage) + - [Standard HTTP router](#standard-http-router) + - [Chi Router and others](#chi-router-and-other-compatible-http-handlers) +- [API](#api) +- [FAQs](#faqs) + - [customizing http error](#customizing-the-error-response) + - [ignoring keys](#ignoring-non-existent-keys-in-the-multipart-request) + - [custom validation logic](#writing-your-custom-validator-logic) ## Installation @@ -172,7 +180,9 @@ Gulter also ships with two storage implementations at the moment: - `DiskStore`: uses a local filesystem backed store to upload files - `CloudinaryStore`: uploads file to cloudinary -## Ignoring non existent keys in the multipart Request +## FAQs + +### 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 @@ -180,7 +190,7 @@ 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 +### Customizing the error response Since Gulter is a middleware that runs, it returns an error to the client if found, this might not match your existing structure, so to configure the response, use the @@ -198,7 +208,7 @@ to define yours. } ``` -## Writing your custom validator logic +### 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 diff --git a/reader.go b/reader.go new file mode 100644 index 0000000..69038fb --- /dev/null +++ b/reader.go @@ -0,0 +1,31 @@ +package gulter + +import ( + "io" + "os" +) + +func ReaderToSeeker(r io.Reader) (io.ReadSeeker, error) { + tmpfile, err := os.CreateTemp("", "upload-") + if err != nil { + return nil, err + } + + _, err = io.Copy(tmpfile, r) + if err != nil { + _ = tmpfile.Close() + _ = os.Remove(tmpfile.Name()) + return nil, err + } + + _, err = tmpfile.Seek(0, 0) + if err != nil { + _ = tmpfile.Close() + _ = os.Remove(tmpfile.Name()) + return nil, err + } + + // Return the file, which implements io.ReadSeeker + // which you can now pass to the gulter uploader + return tmpfile, nil +} diff --git a/storage/s3.go b/storage/s3.go index ee7a6ff..cdde0bc 100644 --- a/storage/s3.go +++ b/storage/s3.go @@ -88,6 +88,7 @@ func (s *S3Store) Close() error { return nil } func (s *S3Store) Upload(ctx context.Context, r io.Reader, opts *gulter.UploadFileOptions, ) (*gulter.UploadedFileMetadata, error) { + b := new(bytes.Buffer) r = io.TeeReader(r, b) @@ -97,12 +98,17 @@ func (s *S3Store) Upload(ctx context.Context, r io.Reader, return nil, err } + seeker, err := gulter.ReaderToSeeker(b) + if err != nil { + return nil, err + } + _, err = s.client.PutObject(ctx, &s3.PutObjectInput{ Bucket: aws.String(s.opts.Bucket), Metadata: opts.Metadata, Key: aws.String(opts.FileName), ACL: s.opts.ACL, - Body: b, + Body: seeker, }) if err != nil { return nil, err