func (ui *UIHandler) serveUploadHelper(rw http.ResponseWriter, req *http.Request) { if ui.root.Storage == nil { httputil.ServeJSONError(rw, httputil.ServerError("No BlobRoot configured")) return } mr, err := httputil.MultipartReader(req) if err != nil { httputil.ServeJSONError(rw, httputil.ServerError("reading body: "+err.Error())) return } var got []*uploadHelperGotItem var modTime types.Time3339 for { part, err := mr.NextPart() if err == io.EOF { break } if err != nil { httputil.ServeJSONError(rw, httputil.ServerError("reading body: "+err.Error())) break } if part.FormName() == "modtime" { payload, err := ioutil.ReadAll(part) if err != nil { log.Printf("ui uploadhelper: unable to read part for modtime: %v", err) continue } modTime = types.ParseTime3339OrZero(string(payload)) continue } fileName := part.FileName() if fileName == "" { continue } br, err := schema.WriteFileFromReaderWithModTime(ui.root.Storage, fileName, modTime.Time(), part) if err != nil { httputil.ServeJSONError(rw, httputil.ServerError("writing to blobserver: "+err.Error())) return } got = append(got, &uploadHelperGotItem{ FileName: part.FileName(), ModTime: modTime, FormName: part.FormName(), FileRef: br, }) } httputil.ReturnJSON(rw, &uploadHelperResponse{Got: got}) }
func handleMultiPartUpload(rw http.ResponseWriter, req *http.Request, blobReceiver blobserver.BlobReceiveConfiger) { res := new(protocol.UploadResponse) if !(req.Method == "POST" && strings.Contains(req.URL.Path, "/camli/upload")) { log.Printf("Inconfigured handler upload handler") httputil.BadRequestError(rw, "Inconfigured handler.") return } receivedBlobs := make([]blob.SizedRef, 0, 10) multipart, err := httputil.MultipartReader(req) if multipart == nil { httputil.BadRequestError(rw, fmt.Sprintf( "Expected multipart/form-data POST request; %v", err)) return } var errBuf bytes.Buffer addError := func(s string) { log.Printf("Client error: %s", s) if errBuf.Len() > 0 { errBuf.WriteByte('\n') } errBuf.WriteString(s) } for { mimePart, err := multipart.NextPart() if err == io.EOF { break } if err != nil { addError(fmt.Sprintf("Error reading multipart section: %v", err)) break } contentDisposition, params, err := mime.ParseMediaType(mimePart.Header.Get("Content-Disposition")) if err != nil { addError("invalid Content-Disposition") break } if contentDisposition != "form-data" { addError(fmt.Sprintf("Expected Content-Disposition of \"form-data\"; got %q", contentDisposition)) break } formName := params["name"] ref, ok := blob.Parse(formName) if !ok { addError(fmt.Sprintf("Ignoring form key %q", formName)) continue } var tooBig int64 = blobserver.MaxBlobSize + 1 var readBytes int64 blobGot, err := blobserver.Receive(blobReceiver, ref, &readerutil.CountingReader{ io.LimitReader(mimePart, tooBig), &readBytes, }) if readBytes == tooBig { err = fmt.Errorf("blob over the limit of %d bytes", blobserver.MaxBlobSize) } if err != nil { addError(fmt.Sprintf("Error receiving blob %v: %v\n", ref, err)) break } log.Printf("Received blob %v\n", blobGot) receivedBlobs = append(receivedBlobs, blobGot) } res.Received = receivedBlobs if req.Header.Get("X-Camlistore-Vivify") == "1" { for _, got := range receivedBlobs { err := vivify(blobReceiver, got) if err != nil { addError(fmt.Sprintf("Error vivifying blob %v: %v\n", got.Ref.String(), err)) } else { rw.Header().Add("X-Camlistore-Vivified", got.Ref.String()) } } } res.ErrorText = errBuf.String() httputil.ReturnJSON(rw, res) }