package main import ( "bytes" "flag" "fmt" "io" "mime/multipart" "net/http" "os" "path/filepath" "strings" ) type Configuration struct { ServerURL string Repo string TaskID string Token string FilePatterns []string Files []string } func parseFlags() Configuration { var cfg Configuration flag.StringVar(&cfg.ServerURL, "url", "http://localhost:9999", "Базовый URL сервера загрузки") flag.StringVar(&cfg.Repo, "repo", "", "Название репозитория (обязательно)") flag.StringVar(&cfg.TaskID, "task", "", "ID задачи (обязательно)") flag.StringVar(&cfg.Token, "token", "", "Токен авторизации (обязательно)") files := flag.String("files", "", "Список путей к файлам или паттернов (разделены запятой) для загрузки (обязательно)") flag.Parse() missing := false if cfg.Repo == "" { fmt.Println("Ошибка: -repo является обязательным") missing = true } if cfg.TaskID == "" { fmt.Println("Ошибка: -task является обязательным") missing = true } if cfg.Token == "" { fmt.Println("Ошибка: -token является обязательным") missing = true } if *files == "" { fmt.Println("Ошибка: -files является обязательным") missing = true } if missing { flag.Usage() os.Exit(1) } cfg.FilePatterns = splitAndTrim(*files, ",") var allFiles []string for _, pattern := range cfg.FilePatterns { matches, err := filepath.Glob(pattern) if err != nil { fmt.Fprintf(os.Stderr, "Неверный паттерн glob '%s': %v\n", pattern, err) continue } if len(matches) == 0 { fmt.Fprintf(os.Stderr, "Предупреждение: Паттерн '%s' не совпадает ни с одним файлом\n", pattern) continue } allFiles = append(allFiles, matches...) } cfg.Files = unique(allFiles) if len(cfg.Files) == 0 { fmt.Println("Ошибка: Не найдено файлов для загрузки после обработки паттернов") os.Exit(1) } return cfg } func splitAndTrim(s, sep string) []string { parts := strings.Split(s, sep) var trimmed []string for _, part := range parts { part = strings.TrimSpace(part) if part != "" { trimmed = append(trimmed, part) } } return trimmed } func unique(input []string) []string { seen := make(map[string]struct{}) var result []string for _, item := range input { if _, exists := seen[item]; !exists { seen[item] = struct{}{} result = append(result, item) } } return result } func createMultipartRequest(url string, files []string, token string) (*http.Request, error) { var buf bytes.Buffer writer := multipart.NewWriter(&buf) for _, filePath := range files { file, err := os.Open(filePath) if err != nil { return nil, fmt.Errorf("не удалось открыть файл %s: %w", filePath, err) } defer file.Close() part, err := writer.CreateFormFile("files", filepath.Base(filePath)) if err != nil { return nil, fmt.Errorf("не удалось создать поле формы для файла %s: %w", filePath, err) } _, err = io.Copy(part, file) if err != nil { return nil, fmt.Errorf("не удалось скопировать содержимое файла %s: %w", filePath, err) } } err := writer.Close() if err != nil { return nil, fmt.Errorf("не удалось закрыть multipart писатель: %w", err) } req, err := http.NewRequest("POST", url, &buf) if err != nil { return nil, fmt.Errorf("не удалось создать HTTP-запрос: %w", err) } req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Authorization", "Bearer "+token) return req, nil } func uploadFiles(cfg Configuration) error { uploadURL := fmt.Sprintf("%s/upload/%s/task/%s", strings.TrimRight(cfg.ServerURL, "/"), cfg.Repo, cfg.TaskID) req, err := createMultipartRequest(uploadURL, cfg.Files, cfg.Token) if err != nil { return fmt.Errorf("не удалось создать multipart-запрос: %w", err) } client := &http.Client{} resp, err := client.Do(req) if err != nil { return fmt.Errorf("не удалось выполнить HTTP-запрос: %w", err) } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("не удалось прочитать тело ответа: %w", err) } if resp.StatusCode != http.StatusOK { return fmt.Errorf("загрузка не удалась с кодом %d: %s", resp.StatusCode, string(respBody)) } fmt.Println("Загрузка успешна:") fmt.Println(string(respBody)) return nil } func main() { cfg := parseFlags() err := uploadFiles(cfg) if err != nil { fmt.Fprintf(os.Stderr, "Ошибка: %v\n", err) os.Exit(1) } }