github.com/blend/go-sdk@v1.20220411.3/webutil/posted_files.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package webutil 9 10 import ( 11 "io" 12 "net/http" 13 14 "github.com/blend/go-sdk/ex" 15 ) 16 17 const ( 18 // DefaultPostedFilesMaxMemory is the maximum post body size we will typically consume. 19 DefaultPostedFilesMaxMemory = 67_108_864 //64mb 20 ) 21 22 // PostedFile is a file that has been posted to an hc endpoint. 23 type PostedFile struct { 24 Key string 25 FileName string 26 Contents []byte 27 ContentType string 28 } 29 30 // PostedFilesOptions are options for the PostedFiles function. 31 type PostedFilesOptions struct { 32 MaxMemory int64 33 ParseMultipartForm bool 34 ParseForm bool 35 } 36 37 // PostedFileOption mutates posted file options. 38 type PostedFileOption func(*PostedFilesOptions) 39 40 // OptPostedFilesMaxMemory sets the max memory for the posted files options (defaults to 64mb). 41 func OptPostedFilesMaxMemory(maxMemory int64) PostedFileOption { 42 return func(pfo *PostedFilesOptions) { pfo.MaxMemory = maxMemory } 43 } 44 45 // OptPostedFilesParseMultipartForm sets if we should parse the multipart form for files (defaults to true). 46 func OptPostedFilesParseMultipartForm(parseMultipartForm bool) PostedFileOption { 47 return func(pfo *PostedFilesOptions) { pfo.ParseMultipartForm = parseMultipartForm } 48 } 49 50 // OptPostedFilesParseForm sets if we should parse the post form for files (defaults to false). 51 func OptPostedFilesParseForm(parseForm bool) PostedFileOption { 52 return func(pfo *PostedFilesOptions) { pfo.ParseForm = parseForm } 53 } 54 55 // PostedFiles returns any files posted to the request. 56 // 57 // The files are held in memory, if you need to stream the files out because they may be large, 58 // you should use the `*net/http.Request.FormFile(...)` function directly instead of this method. 59 func PostedFiles(r *http.Request, opts ...PostedFileOption) ([]PostedFile, error) { 60 var files []PostedFile 61 62 options := PostedFilesOptions{ 63 MaxMemory: DefaultPostedFilesMaxMemory, 64 ParseMultipartForm: true, 65 ParseForm: false, 66 } 67 for _, opt := range opts { 68 opt(&options) 69 } 70 71 if options.ParseMultipartForm { 72 if err := r.ParseMultipartForm(options.MaxMemory); err != nil { 73 return nil, ex.New(err) 74 } 75 for key := range r.MultipartForm.File { 76 file, err := readPostedFile(r, key) 77 if err != nil { 78 return nil, err 79 } 80 files = append(files, *file) 81 } 82 } 83 if options.ParseForm { 84 if err := r.ParseForm(); err != nil { 85 return nil, ex.New(err) 86 } 87 for key := range r.PostForm { 88 file, err := readPostedFile(r, key) 89 if err != nil { 90 return nil, err 91 } 92 files = append(files, *file) 93 } 94 } 95 return files, nil 96 } 97 98 func readPostedFile(r *http.Request, key string) (*PostedFile, error) { 99 fileReader, fileHeader, err := r.FormFile(key) 100 if err != nil { 101 return nil, ex.New(err) 102 } 103 defer fileReader.Close() 104 fileContents, err := io.ReadAll(fileReader) 105 if err != nil { 106 return nil, ex.New(err) 107 } 108 return &PostedFile{Key: key, FileName: fileHeader.Filename, Contents: fileContents}, nil 109 }