|
1 | 1 | package httpclient
|
2 | 2 |
|
3 | 3 | import (
|
4 |
| - "bytes" |
5 | 4 | "context"
|
6 | 5 | "encoding/base64"
|
7 | 6 | "fmt"
|
8 | 7 | "io"
|
9 | 8 | "mime/multipart"
|
10 | 9 | "net/http"
|
11 | 10 | "net/textproto"
|
12 |
| - "net/url" |
13 | 11 | "os"
|
14 | 12 | "path/filepath"
|
15 |
| - "strings" |
16 | 13 | "sync"
|
17 | 14 | "time"
|
18 | 15 |
|
@@ -307,88 +304,6 @@ func addFormField(writer *multipart.Writer, key, val string, sugar *zap.SugaredL
|
307 | 304 | return nil
|
308 | 305 | }
|
309 | 306 |
|
310 |
| -// addFilePart adds a base64 encoded file part to the multipart writer with the provided field name and file path. |
311 |
| -// This function opens the specified file, sets the appropriate content type and headers, and adds it to the multipart writer. |
312 |
| -// Parameters: |
313 |
| -// - writer: The multipart writer used to construct the multipart request body. |
314 |
| -// - fieldName: The field name for the file part. |
315 |
| -// - filePath: The path to the file to be included in the request. |
316 |
| -// - fileContentTypes: A map specifying the content type for each file part. The key is the field name and the value is the |
317 |
| -// content type (e.g., "image/jpeg"). |
318 |
| -// - formDataPartHeaders: A map specifying custom headers for each part of the multipart form data. The key is the field name |
319 |
| -// and the value is an http.Header containing the headers for that part. |
320 |
| -// - sugar: An instance of a logger implementing the logger.Logger interface, used to sugar informational messages, warnings, |
321 |
| -// and errors encountered during the addition of the file part. |
322 |
| -// |
323 |
| -// Returns: |
324 |
| -// - error: An error object indicating failure during the addition of the file part. This could be due to issues such as |
325 |
| -// file reading errors or multipart writer errors. |
326 |
| -// func addFilePart(writer *multipart.Writer, fieldName, filePath string, fileContentTypes map[string]string, formDataPartHeaders map[string]http.Header, sugar *zap.SugaredLogger) error { |
327 |
| -// file, err := os.Open(filePath) |
328 |
| -// if err != nil { |
329 |
| -// sugar.Errorw("Failed to open file", zap.String("filePath", filePath), zap.Error(err)) |
330 |
| -// return err |
331 |
| -// } |
332 |
| -// defer file.Close() |
333 |
| - |
334 |
| -// // Default fileContentType |
335 |
| -// contentType := "application/octet-stream" |
336 |
| -// if ct, ok := fileContentTypes[fieldName]; ok { |
337 |
| -// contentType = ct |
338 |
| -// } |
339 |
| - |
340 |
| -// header := setFormDataPartHeader(fieldName, filepath.Base(filePath), contentType, formDataPartHeaders[fieldName]) |
341 |
| - |
342 |
| -// part, err := writer.CreatePart(header) |
343 |
| -// if err != nil { |
344 |
| -// sugar.Errorw("Failed to create form file part", zap.String("fieldName", fieldName), zap.Error(err)) |
345 |
| -// return err |
346 |
| -// } |
347 |
| - |
348 |
| -// encoder := base64.NewEncoder(base64.StdEncoding, part) |
349 |
| -// defer encoder.Close() |
350 |
| - |
351 |
| -// fileSize, err := file.Stat() |
352 |
| -// if err != nil { |
353 |
| -// sugar.Errorw("Failed to get file info", zap.String("filePath", filePath), zap.Error(err)) |
354 |
| -// return err |
355 |
| -// } |
356 |
| - |
357 |
| -// progressLogger := logUploadProgress(file, fileSize.Size(), sugar) |
358 |
| -// uploadState := &UploadState{} |
359 |
| -// if err := chunkFileUpload(file, encoder, progressLogger, uploadState, sugar); err != nil { |
360 |
| -// sugar.Errorw("Failed to copy file content", zap.String("filePath", filePath), zap.Error(err)) |
361 |
| -// return err |
362 |
| -// } |
363 |
| - |
364 |
| -// return nil |
365 |
| -// } |
366 |
| - |
367 |
| -// setFormDataPartHeader creates a textproto.MIMEHeader for a form data field with the provided field name, file name, content type, and custom headers. |
368 |
| -// This function constructs the MIME headers for a multipart form data part, including the content disposition, content type, |
369 |
| -// and any custom headers specified. |
370 |
| -// Parameters: |
371 |
| -// - fieldname: The name of the form field. |
372 |
| -// - filename: The name of the file being uploaded (if applicable). |
373 |
| -// - contentType: The content type of the form data part (e.g., "image/jpeg"). |
374 |
| -// - customHeaders: A map of custom headers to be added to the form data part. The key is the header name and the value is the |
375 |
| -// header value. |
376 |
| -// |
377 |
| -// Returns: |
378 |
| -// - textproto.MIMEHeader: The constructed MIME header for the form data part. |
379 |
| -// func setFormDataPartHeader(fieldname, filename, contentType string, customHeaders http.Header) textproto.MIMEHeader { |
380 |
| -// header := textproto.MIMEHeader{} |
381 |
| -// header.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, fieldname, filename)) |
382 |
| -// header.Set("Content-Type", contentType) |
383 |
| -// header.Set("Content-Transfer-Encoding", "base64") |
384 |
| -// for key, values := range customHeaders { |
385 |
| -// for _, value := range values { |
386 |
| -// header.Add(key, value) |
387 |
| -// } |
388 |
| -// } |
389 |
| -// return header |
390 |
| -// } |
391 |
| - |
392 | 307 | // chunkFileUpload reads the file upload into chunks and writes it to the writer.
|
393 | 308 | // This function reads the file in chunks and writes it to the provided writer, allowing for progress logging during the upload.
|
394 | 309 | // The chunk size is set to 8192 KB (8 MB) by default. This is a common chunk size used for file uploads to cloud storage services.
|
@@ -508,111 +423,3 @@ func logUploadProgress(file *os.File, fileSize int64, sugar *zap.SugaredLogger)
|
508 | 423 | }
|
509 | 424 | }
|
510 | 425 | }
|
511 |
| - |
512 |
| -// DoImageMultiPartUpload performs a multipart request with a specifically formatted payload. |
513 |
| -// This is designed for APIs that expect a very specific multipart format, where the payload |
514 |
| -// needs to be constructed manually rather than using the standard multipart writer. |
515 |
| -func (c *Client) DoImageMultiPartUpload(method, endpoint string, fileName string, base64Data string, customBoundary string, out interface{}) (*http.Response, error) { |
516 |
| - c.Sugar.Infow("Starting DoImageMultiPartUpload", |
517 |
| - zap.String("method", method), |
518 |
| - zap.String("endpoint", endpoint), |
519 |
| - zap.String("fileName", fileName), |
520 |
| - zap.String("boundary", customBoundary)) |
521 |
| - |
522 |
| - // URL encode the filename for both the Content-Disposition and data prefix |
523 |
| - encodedFileName := url.QueryEscape(fileName) |
524 |
| - c.Sugar.Debugw("URL encoded filename", zap.String("encodedFileName", encodedFileName)) |
525 |
| - |
526 |
| - // Construct payload exactly like the example |
527 |
| - payload := fmt.Sprintf("%s\r\n"+ |
528 |
| - "Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n"+ |
529 |
| - "Content-Type: image/png\r\n\r\n"+ |
530 |
| - "data:image/png;name=%s;base64,%s\r\n"+ |
531 |
| - "%s-", |
532 |
| - customBoundary, |
533 |
| - encodedFileName, |
534 |
| - encodedFileName, |
535 |
| - base64Data, |
536 |
| - customBoundary) |
537 |
| - |
538 |
| - // Create truncated version of payload for logging |
539 |
| - truncatedPayload := payload |
540 |
| - if len(base64Data) > 100 { |
541 |
| - // Find the position of base64 data in the payload |
542 |
| - base64Start := strings.Index(payload, ";base64,") + 8 |
543 |
| - if base64Start > 0 { |
544 |
| - truncatedPayload = payload[:base64Start] + "[BASE64_DATA_LENGTH: " + |
545 |
| - fmt.Sprintf("%d", len(base64Data)) + "]\r\n" + |
546 |
| - customBoundary + "-" |
547 |
| - } |
548 |
| - } |
549 |
| - |
550 |
| - c.Sugar.Debugw("Constructed request payload", |
551 |
| - zap.String("payload", truncatedPayload)) |
552 |
| - |
553 |
| - url := (*c.Integration).GetFQDN() + endpoint |
554 |
| - c.Sugar.Debugw("Full request URL", zap.String("url", url)) |
555 |
| - |
556 |
| - // Create request with string payload |
557 |
| - req, err := http.NewRequest(method, url, strings.NewReader(payload)) |
558 |
| - if err != nil { |
559 |
| - c.Sugar.Errorw("Failed to create request", zap.Error(err)) |
560 |
| - return nil, fmt.Errorf("failed to create request: %v", err) |
561 |
| - } |
562 |
| - |
563 |
| - // Set headers exactly as in example |
564 |
| - req.Header.Set("accept", "application/json") |
565 |
| - req.Header.Set("content-type", fmt.Sprintf("multipart/form-data; boundary=%s", strings.TrimPrefix(customBoundary, "---"))) |
566 |
| - |
567 |
| - c.Sugar.Debugw("Initial headers", |
568 |
| - zap.Any("headers", req.Header), |
569 |
| - zap.String("accept", req.Header.Get("accept")), |
570 |
| - zap.String("content-type", req.Header.Get("content-type"))) |
571 |
| - |
572 |
| - // Store initial headers |
573 |
| - contentType := req.Header.Get("content-type") |
574 |
| - accept := req.Header.Get("accept") |
575 |
| - |
576 |
| - // Apply auth |
577 |
| - (*c.Integration).PrepRequestParamsAndAuth(req) |
578 |
| - |
579 |
| - // Restore and log final headers |
580 |
| - req.Header.Set("accept", accept) |
581 |
| - req.Header.Set("content-type", contentType) |
582 |
| - |
583 |
| - c.Sugar.Infow("Final request headers", |
584 |
| - zap.Any("headers", req.Header), |
585 |
| - zap.String("accept", req.Header.Get("accept")), |
586 |
| - zap.String("content-type", req.Header.Get("content-type"))) |
587 |
| - |
588 |
| - // Send the request |
589 |
| - resp, err := c.http.Do(req) |
590 |
| - if err != nil { |
591 |
| - return nil, fmt.Errorf("failed to send request: %v", err) |
592 |
| - } |
593 |
| - |
594 |
| - c.Sugar.Debugw("Response received", |
595 |
| - zap.Int("statusCode", resp.StatusCode), |
596 |
| - zap.Any("responseHeaders", resp.Header)) |
597 |
| - |
598 |
| - // Handle response |
599 |
| - if resp.StatusCode >= 200 && resp.StatusCode < 300 { |
600 |
| - return resp, response.HandleAPISuccessResponse(resp, out, c.Sugar) |
601 |
| - } |
602 |
| - |
603 |
| - // For error responses, try to log the response body |
604 |
| - if resp.Body != nil { |
605 |
| - bodyBytes, err := io.ReadAll(resp.Body) |
606 |
| - if err != nil { |
607 |
| - c.Sugar.Warnw("Failed to read error response body", zap.Error(err)) |
608 |
| - } else { |
609 |
| - c.Sugar.Errorw("Request failed", |
610 |
| - zap.Int("statusCode", resp.StatusCode), |
611 |
| - zap.String("responseBody", string(bodyBytes))) |
612 |
| - // Create new reader with same data for error handler |
613 |
| - resp.Body = io.NopCloser(bytes.NewReader(bodyBytes)) |
614 |
| - } |
615 |
| - } |
616 |
| - |
617 |
| - return resp, response.HandleAPIErrorResponse(resp, c.Sugar) |
618 |
| -} |
0 commit comments