164 lines
3.7 KiB
Go
164 lines
3.7 KiB
Go
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"mime"
|
|
"net/http"
|
|
"net/url"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
func RemoveItem[S ~[]E, E comparable](s S, values ...E) S {
|
|
result := make(S, 0, len(s))
|
|
outer:
|
|
for _, item := range s {
|
|
for _, v := range values {
|
|
if item == v {
|
|
continue outer
|
|
}
|
|
}
|
|
result = append(result, item)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func Contains(slice []string, value string) bool {
|
|
for _, item := range slice {
|
|
if item == value {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func GenerateHash(data string) string {
|
|
// Simple hash generation using a basic algorithm (for demonstration purposes)
|
|
_hash := 0
|
|
for _, char := range data {
|
|
_hash = (_hash*31 + int(char)) % 1000003 // Simple hash function
|
|
}
|
|
return string(rune(_hash))
|
|
}
|
|
|
|
func DownloadFile(url string) (string, []byte, error) {
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
return "", nil, fmt.Errorf("failed to download file: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return "", nil, fmt.Errorf("failed to download file: status code %d", resp.StatusCode)
|
|
}
|
|
|
|
filename := getFilenameFromResponse(resp, url)
|
|
|
|
data, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return "", nil, fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
|
|
return filename, data, nil
|
|
}
|
|
|
|
func getFilenameFromResponse(resp *http.Response, originalURL string) string {
|
|
// 1. Try Content-Disposition header
|
|
if cd := resp.Header.Get("Content-Disposition"); cd != "" {
|
|
if _, params, err := mime.ParseMediaType(cd); err == nil {
|
|
if filename := params["filename"]; filename != "" {
|
|
return filename
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Try to decode URL-encoded filename from Content-Disposition
|
|
if cd := resp.Header.Get("Content-Disposition"); cd != "" {
|
|
if strings.Contains(cd, "filename*=") {
|
|
// Handle RFC 5987 encoded filenames
|
|
parts := strings.Split(cd, "filename*=")
|
|
if len(parts) > 1 {
|
|
encoded := strings.Trim(parts[1], `"`)
|
|
if strings.HasPrefix(encoded, "UTF-8''") {
|
|
if decoded, err := url.QueryUnescape(encoded[7:]); err == nil {
|
|
return decoded
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. Fall back to URL path
|
|
if parsedURL, err := url.Parse(originalURL); err == nil {
|
|
if filename := filepath.Base(parsedURL.Path); filename != "." && filename != "/" {
|
|
// URL decode the filename
|
|
if decoded, err := url.QueryUnescape(filename); err == nil {
|
|
return decoded
|
|
}
|
|
return filename
|
|
}
|
|
}
|
|
|
|
// 4. Default filename
|
|
return "downloaded_file"
|
|
}
|
|
|
|
func ValidateServiceURL(urlStr string) error {
|
|
if urlStr == "" {
|
|
return fmt.Errorf("URL cannot be empty")
|
|
}
|
|
|
|
// Try parsing as full URL first
|
|
u, err := url.Parse(urlStr)
|
|
if err == nil && u.Scheme != "" && u.Host != "" {
|
|
// It's a full URL, validate scheme
|
|
if u.Scheme != "http" && u.Scheme != "https" {
|
|
return fmt.Errorf("URL scheme must be http or https")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Check if it's a host:port format (no scheme)
|
|
if strings.Contains(urlStr, ":") && !strings.Contains(urlStr, "://") {
|
|
// Try parsing with http:// prefix
|
|
testURL := "http://" + urlStr
|
|
u, err := url.Parse(testURL)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid host:port format: %w", err)
|
|
}
|
|
|
|
if u.Host == "" {
|
|
return fmt.Errorf("host is required in host:port format")
|
|
}
|
|
|
|
// Validate port number
|
|
if u.Port() == "" {
|
|
return fmt.Errorf("port is required in host:port format")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("invalid URL format: %s", urlStr)
|
|
}
|
|
|
|
func ExtractFilenameFromURL(rawURL string) string {
|
|
// Parse the URL
|
|
parsedURL, err := url.Parse(rawURL)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
// Get the base filename from path
|
|
filename := path.Base(parsedURL.Path)
|
|
|
|
// Handle edge cases
|
|
if filename == "/" || filename == "." || filename == "" {
|
|
return ""
|
|
}
|
|
|
|
return filename
|
|
}
|