(w http.ResponseWriter, r *http.Request)
| 303 | } |
| 304 | |
| 305 | func (h *handler) handleUpload(w http.ResponseWriter, r *http.Request) { |
| 306 | ctx := r.Context() |
| 307 | if r.Method != "POST" { |
| 308 | http.Error(w, "not a POST", http.StatusMethodNotAllowed) |
| 309 | return |
| 310 | } |
| 311 | |
| 312 | mr, err := r.MultipartReader() |
| 313 | if err != nil { |
| 314 | httputil.ServeError(w, r, err) |
| 315 | return |
| 316 | } |
| 317 | |
| 318 | var br blob.Ref |
| 319 | var fileName string |
| 320 | var creation time.Time |
| 321 | cr := countingReader{} |
| 322 | for { |
| 323 | part, err := mr.NextPart() |
| 324 | if err == io.EOF { |
| 325 | break |
| 326 | } |
| 327 | if err != nil { |
| 328 | httputil.ServeError(w, r, err) |
| 329 | return |
| 330 | } |
| 331 | name := part.FileName() |
| 332 | if name == "" { |
| 333 | // Let the user provide a creation time (for migrating Brad's data) |
| 334 | if part.FormName() != "creation" { |
| 335 | logf("form field %q provided in upload, but ignored", part.FormName()) |
| 336 | continue |
| 337 | } |
| 338 | creationParam, err := io.ReadAll(part) |
| 339 | if err != nil { |
| 340 | httputil.ServeError(w, r, fmt.Errorf("could not read provided creation time: %v", err)) |
| 341 | return |
| 342 | } |
| 343 | creation, err = time.Parse(time.RFC3339, string(creationParam)) |
| 344 | if err != nil { |
| 345 | httputil.ServeError(w, r, fmt.Errorf("could not parse provided creation time %q: %v", creationParam, err)) |
| 346 | return |
| 347 | } |
| 348 | |
| 349 | continue |
| 350 | } |
| 351 | fileName = path.Base(name) |
| 352 | cr.r = part |
| 353 | br, err = h.cl.UploadFile(ctx, fileName, &cr, nil) |
| 354 | if err != nil { |
| 355 | httputil.ServeError(w, r, fmt.Errorf("could not write %v to blobserver: %v", fileName, err)) |
| 356 | return |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | creationProvided := true |
| 361 | if creation.IsZero() { |
| 362 | creationProvided = false |
nothing calls this directly
no test coverage detected