Go语言实现对象存储——下载任意图片,保存到阿里云存储,并将URL保存到数据库。
提前到阿里云获取三个月免费的对象存储资源
用户登录名称 xxx.onaliyun.com
AccessKey ID
AccessKey Secret
go mod init your_project_name
go mod tidy
go get github.com/aliyun/aliyun-oss-go-sdk/oss
go get github.com/go-sql-driver/mysql
使用阿里云OSS SDK将图片上传到阿里云存储,并将URL保存到MySQL数据库。
项目结构
.
├── main.go
├── config
│ └── config.go
├── database
│ └── database.go
├── oss
│ └── oss.go
└── utils└── utils.go
1. 配置文件 (config/config.go)
package configconst (// 阿里云OSS配置OSSEndpoint = "oss-cn-hangzhou.aliyuncs.com" // 根据你的实际区域修改OSSBucketName = "your-bucket-name" // 替换为你的bucket名称OSSAccessKeyId = "LTAI5tLi17zqqJyZdqYLE24p"OSSAccessKeySecret = "vxvWYpfQfBErvWWNCXrP7aWxiZV9Pi"// 数据库配置DBDriver = "mysql"DBUser = "remote_user"DBPassword = "Sctl@123456"DBHost = "localhost"DBPort = "3306"DBName = "test2"// 本地临时存储路径LocalTempDir = "./temp"
)
2. 数据库操作 (database/database.go)
package databaseimport ("database/sql""fmt""log""time"_ "github.com/go-sql-driver/mysql""your_project_name/config" // 替换为你的项目名
)var DB *sql.DBfunc InitDB() error {var err errordsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8",config.DBUser,config.DBPassword,config.DBHost,config.DBPort,config.DBName)DB, err = sql.Open(config.DBDriver, dsn)if err != nil {return fmt.Errorf("failed to open database: %v", err)}// 测试数据库连接if err = DB.Ping(); err != nil {return fmt.Errorf("failed to ping database: %v", err)}// 创建表(如果不存在)createTableSQL := `CREATE TABLE IF NOT EXISTS images (id INT AUTO_INCREMENT PRIMARY KEY,add_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,url VARCHAR(255) NOT NULL)`_, err = DB.Exec(createTableSQL)if err != nil {return fmt.Errorf("failed to create table: %v", err)}log.Println("Database initialized successfully")return nil
}func SaveImageURL(url string) error {insertSQL := `INSERT INTO images (url) VALUES (?)`_, err := DB.Exec(insertSQL, url)if err != nil {return fmt.Errorf("failed to save image URL: %v", err)}return nil
}
3. OSS操作 (oss/oss.go)
package ossimport ("fmt""io""log""time""github.com/aliyun/aliyun-oss-go-sdk/oss""your_project_name/config" // 替换为你的项目名
)var Client *oss.Client
var Bucket *oss.Bucketfunc InitOSS() error {var err error// 创建OSSClient实例Client, err = oss.New(config.OSSEndpoint, config.OSSAccessKeyId, config.OSSAccessKeySecret)if err != nil {return fmt.Errorf("failed to create OSS client: %v", err)}// 获取存储空间Bucket, err = Client.Bucket(config.OSSBucketName)if err != nil {return fmt.Errorf("failed to get bucket: %v", err)}log.Println("OSS initialized successfully")return nil
}func UploadFileToOSS(localFilePath, objectName string) (string, error) {// 上传文件err := Bucket.PutObjectFromFile(objectName, localFilePath)if err != nil {return "", fmt.Errorf("failed to upload file to OSS: %v", err)}// 获取文件URLsignedURL, err := Bucket.SignURL(objectName, oss.HTTPGet, 3600 * 24 * 365) // 1年有效期的URLif err != nil {return "", fmt.Errorf("failed to generate signed URL: %v", err)}return signedURL, nil
}func GenerateObjectName(filename string) string {// 使用时间戳+随机数生成唯一的文件名timestamp := time.Now().UnixNano()return fmt.Sprintf("images/%d_%s", timestamp, filename)
}
4. 工具函数 (utils/utils.go)
package utilsimport ("fmt""io""net/http""os""path/filepath"
)func DownloadImage(imageURL, localPath string) error {// 创建HTTP GET请求resp, err := http.Get(imageURL)if err != nil {return fmt.Errorf("failed to download image: %v", err)}defer resp.Body.Close()// 检查响应状态码if resp.StatusCode != http.StatusOK {return fmt.Errorf("bad status: %s", resp.Status)}// 创建本地文件out, err := os.Create(localPath)if err != nil {return fmt.Errorf("failed to create local file: %v", err)}defer out.Close()// 将响应体复制到文件_, err = io.Copy(out, resp.Body)if err != nil {return fmt.Errorf("failed to save image to file: %v", err)}return nil
}func EnsureDir(dirPath string) error {// 检查目录是否存在if _, err := os.Stat(dirPath); os.IsNotExist(err) {// 创建目录err = os.MkdirAll(dirPath, 0755)if err != nil {return fmt.Errorf("failed to create directory: %v", err)}}return nil
}func GetFilenameFromURL(url string) string {// 从URL中提取文件名// 简单实现,实际可能需要更复杂的处理return filepath.Base(url)
}
5. 主程序 (main.go)
package mainimport ("fmt""log""os""path/filepath""your_project_name/config" // 替换为你的项目名"your_project_name/database" // 替换为你的项目名"your_project_name/oss" // 替换为你的项目名"your_project_name/utils" // 替换为你的项目名
)func main() {// 初始化配置fmt.Println("Initializing application...")// 初始化数据库if err := database.InitDB(); err != nil {log.Fatalf("Failed to initialize database: %v", err)}defer database.DB.Close()// 初始化OSSif err := oss.InitOSS(); err != nil {log.Fatalf("Failed to initialize OSS: %v", err)}// 示例图片URL (可以替换为从命令行参数或配置中获取)imageURL := "https://example.com/sample.jpg" // 替换为实际的图片URL// 确保临时目录存在if err := utils.EnsureDir(config.LocalTempDir); err != nil {log.Fatalf("Failed to ensure temp directory: %v", err)}// 从URL获取文件名filename := utils.GetFilenameFromURL(imageURL)localFilePath := filepath.Join(config.LocalTempDir, filename)// 1. 下载图片到本地fmt.Printf("Downloading image from %s...\n", imageURL)if err := utils.DownloadImage(imageURL, localFilePath); err != nil {log.Fatalf("Failed to download image: %v", err)}fmt.Println("Image downloaded successfully")// 2. 上传图片到OSSobjectName := oss.GenerateObjectName(filename)fmt.Printf("Uploading image to OSS as %s...\n", objectName)ossURL, err := oss.UploadFileToOSS(localFilePath, objectName)if err != nil {log.Fatalf("Failed to upload image to OSS: %v", err)}fmt.Printf("Image uploaded to OSS successfully. URL: %s\n", ossURL)// 3. 保存URL到数据库fmt.Println("Saving URL to database...")if err := database.SaveImageURL(ossURL); err != nil {log.Fatalf("Failed to save URL to database: %v", err)}fmt.Println("URL saved to database successfully")// 清理临时文件if err := os.Remove(localFilePath); err != nil {log.Printf("Warning: failed to remove temporary file: %v", err)}fmt.Println("Process completed successfully")
}
6. 项目依赖
你需要安装以下依赖:
go get github.com/aliyun/aliyun-oss-go-sdk/oss
go get github.com/go-sql-driver/mysql
7. 使用说明
- 将上述代码按照项目结构创建好文件
- 替换
your_project_name
为你的实际项目名 - 修改
config/config.go
中的OSSBucketName
为你的实际bucket名称 - 确保你的阿里云OSS bucket已经创建,并且有正确的权限
- 确保MySQL数据库已经创建,并且有正确的访问权限
- 运行程序:
go run main.go
8. 注意事项
- 安全性:在实际生产环境中,不要将AccessKey直接硬编码在代码中,应该使用环境变量或配置管理工具
- 错误处理:代码中已经包含了基本的错误处理,但在生产环境中可能需要更详细的日志记录
- 并发处理:如果需要处理多个图片,可以考虑使用goroutine并发处理
- 文件名生成:当前实现使用时间戳+随机数生成文件名,可以根据需求调整
- URL有效期:当前实现生成的是1年有效期的URL,可以根据需求调整