package contract import ( "errors" "io" "os" "sync" ) // File is a request-scoped upload, staged on disk using a bounded copy buffer. // Name and ContentType are untrusted client metadata. Open streams content; // readers and the temporary file are closed when the generated handler returns. // Persist wanted content before returning. Retaining File does not extend its lifetime. type File struct { Name string ContentType string Size int64 state *uploadState } type uploadState struct { mu sync.Mutex path string closed bool readers map[*uploadReader]bool } type uploadReader struct { file *os.File once sync.Once err error state *uploadState } // Present reports whether the optional upload was supplied. func (f File) Present() bool { return f.state != nil } func (f File) Open() (io.ReadCloser, error) { if f.state == nil { return nil, errors.New("upload file is absent") } s := f.state s.mu.Lock() defer s.mu.Unlock() if s.closed { return nil, errors.New("upload file is closed") } file, err := os.Open(s.path) if err != nil { return nil, err } r := &uploadReader{file: file, state: s} s.readers[r] = true return r, nil } func (r *uploadReader) Read(p []byte) (int, error) { return r.file.Read(p) } func (r *uploadReader) Close() error { r.once.Do(func() { r.err = r.file.Close(); r.state.mu.Lock(); delete(r.state.readers, r); r.state.mu.Unlock() }) return r.err } func (f File) cleanup() error { if f.state == nil { return nil } s := f.state s.mu.Lock() if s.closed { s.mu.Unlock() return nil } s.closed = true readers := make([]*uploadReader, 0, len(s.readers)) for r := range s.readers { readers = append(readers, r) } s.mu.Unlock() var err error for _, r := range readers { err = errors.Join(err, r.Close()) } return errors.Join(err, os.Remove(s.path)) }