【原创】从s3桶将对象导入ES建立索引,以便快速查找文件

总体功能:

这段程序的作用是:

从指定的S3桶中读取所有对象的元数据(文件名、大小、最后修改时间、存储类型、ETag等),并把这些信息写入到Elasticsearch(ES)中,建立索引,以便后续可以快速搜索和检索S3中的文件信息。


各模块详细解析:

1. readConfig
  • 从本地的 config.ini 配置文件中读取 S3 和 Elasticsearch 的连接信息,比如 S3的桶名、访问秘钥,ES的地址、账号密码、索引名等。

2. getS3ETag
  • 给定一个文件名(Key),向S3发送 HeadObject 请求,单独获取该文件的 ETag(一般用于文件完整性校验或者去重标识)。

  • 特别处理了返回的ETag,把多余的引号去掉。


3. fetchS3Files
  • 核心逻辑

  • 通过 S3 的 ListObjectsV2Paginator分页地遍历桶中的所有对象。

  • 对每一个对象:

    • 读取文件名(Key)、大小、最后修改时间、存储类型。

    • 调用 getS3ETag 补充获取 ETag。

    • 把这些元数据组织成一个 JSON 格式的文档。

    • 通过 esapi.IndexRequest 把文档写入到Elasticsearch的指定索引中。


4. main
  • 先读取配置文件。

  • 初始化 S3 客户端(带自定义 Endpoint,例如MinIO、私有S3等),并提供静态访问密钥。

  • 初始化 Elasticsearch 客户端(支持跳过TLS证书验证,适合开发环境)。

  • 调用 fetchS3Files,正式开始批量导入S3文件元数据到ES。


特别注意:

  • InsecureSkipVerify: true 表示 跳过SSL证书校验 ,这个设置在生产环境是不安全的,一般仅用于开发测试。

  • 每导入一个S3对象,都会立即 Refresh 索引,确保数据立刻可搜索,但也会增加ES压力,批量模式可以更高效。

  • 如果S3里文件特别多,可以考虑加并发处理或限制速率,否则会慢。


总结一句话:

这段程序实现了 自动同步S3文件列表到Elasticsearch索引,方便对存储桶中的文件进行快速搜索和查询

复制代码
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/credentials"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/elastic/go-elasticsearch/v8"
	"github.com/elastic/go-elasticsearch/v8/esapi"
	"gopkg.in/ini.v1"
	"log"
	"crypto/tls"
	"net/http"
	"time"
)

type S3Config struct {
	BucketName  string
	AccessKey   string
	SecretKey   string
	EndpointURL string
}

type ESConfig struct {
	Host       string
	User       string
	Pass       string
	IndexName  string
	SearchType string
}

func readConfig() (S3Config, ESConfig) {
	cfg, err := ini.Load("config.ini")
	if err != nil {
		log.Fatalf("无法读取配置文件: %v", err)
	}

	s3Cfg := S3Config{
		BucketName:  cfg.Section("s3").Key("bucket_name").String(),
		AccessKey:   cfg.Section("s3").Key("access_key").String(),
		SecretKey:   cfg.Section("s3").Key("secret_key").String(),
		EndpointURL: cfg.Section("s3").Key("endpoint_url").String(),
	}

	esCfg := ESConfig{
		Host:       cfg.Section("elasticsearch").Key("host").String(),
		User:       cfg.Section("elasticsearch").Key("user").String(),
		Pass:       cfg.Section("elasticsearch").Key("password").String(),
		IndexName:  cfg.Section("elasticsearch").Key("index_name").String(),
		SearchType: cfg.Section("elasticsearch").Key("search_type").String(),
	}

	return s3Cfg, esCfg
}

func getS3ETag(s3Client *s3.Client, bucketName, fileKey string) string {
	resp, err := s3Client.HeadObject(context.TODO(), &s3.HeadObjectInput{
		Bucket: aws.String(bucketName),
		Key:    aws.String(fileKey),
	})
	if err != nil {
		log.Printf("获取 %s 的ETag失败: %v", fileKey, err)
		return ""
	}
	etag := aws.ToString(resp.ETag)
	if len(etag) > 0 && etag[0] == '"' && etag[len(etag)-1] == '"' {
		etag = etag[1 : len(etag)-1]
	}
	return etag
}

func fetchS3Files(s3Client *s3.Client, esClient *elasticsearch.Client, bucketName, indexName string) {
	paginator := s3.NewListObjectsV2Paginator(s3Client, &s3.ListObjectsV2Input{
		Bucket: aws.String(bucketName),
	})

	for paginator.HasMorePages() {
		page, err := paginator.NextPage(context.TODO())
		if err != nil {
			log.Printf("获取S3文件列表页失败: %v", err)
			continue
		}

		for _, obj := range page.Contents {
			fileKey := aws.ToString(obj.Key)
			log.Printf("导入索引:",fileKey)
			fileSize := aws.ToInt64(obj.Size)
			lastModified := obj.LastModified
			storageClass := string(obj.StorageClass) // 修复点
			etag := getS3ETag(s3Client, bucketName, fileKey)

			fileData := map[string]interface{}{
				"file_key":      fileKey,
				"file_size":     fileSize,
				"last_modified": lastModified,
				"storage_class": storageClass,
				"etag":          etag,
			}

			fileDataJSON, err := json.Marshal(fileData)
			if err != nil {
				log.Printf("将文件数据转换为JSON失败: %v", err)
				continue
			}

			req := esapi.IndexRequest{
				Index:   indexName,
				Body:    bytes.NewReader(fileDataJSON), // 修复点
				Refresh: "true",
			}
			resp, err := req.Do(context.TODO(), esClient)
			if err != nil {
				log.Printf("将文件数据索引到Elasticsearch失败: %v", err)
				continue
			}
			defer resp.Body.Close()
		}
	}
	fmt.Println("S3 文件索引完成")
}

func main() {
	s3Cfg, esCfg := readConfig()

	customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
		return aws.Endpoint{
			URL:               s3Cfg.EndpointURL,
			SigningRegion:     "us-east-1", // 替换为你的实际region
			HostnameImmutable: true,
		}, nil
	})

	awsCfg, err := config.LoadDefaultConfig(context.TODO(),
		config.WithRegion("us-east-1"), // 替换为你的实际region
		config.WithEndpointResolverWithOptions(customResolver),
		config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
			s3Cfg.AccessKey,
			s3Cfg.SecretKey,
			"",
		)),
	)
	if err != nil {
		log.Fatalf("无法加载S3配置: %v", err)
	}
	s3Client := s3.NewFromConfig(awsCfg)

	esCfgOptions := elasticsearch.Config{
		Addresses: []string{esCfg.Host},
		Username:  esCfg.User,
		Password:  esCfg.Pass,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true, // ⚠️ 跳过证书校验(不安全,仅限开发)
			},
			ResponseHeaderTimeout: 10 * time.Second,
		},
	}

	esClient, err := elasticsearch.NewClient(esCfgOptions)
	if err != nil {
		log.Fatalf("无法创建Elasticsearch客户端: %v", err)
	}

	fetchS3Files(s3Client, esClient, s3Cfg.BucketName, esCfg.IndexName)
}

配置文件config.ini

复制代码
[elasticsearch]
host = https://localhost:9200
user = elastic
password = U********uq
index_name = jyzx_s3_files
search_type = wildcard

[s3]
bucket_name = fzsjyzx
access_key = V4*****6DB
secret_key = lHdmyi5*********UjlS
endpoint_url = http://172.20.1.18:7480/
相关推荐
2501_9247949011 小时前
企业AI转型为何难?——从“不敢用”到“用得稳”的路径重构
大数据·人工智能·重构
Tezign_space11 小时前
小红书内容运营工具怎么选?专业视角拆解优质工具核心标准
大数据·人工智能·内容运营
康实训11 小时前
养老实训室建设标准指南
大数据·人工智能·实训室·养老实训室·实训室建设
semantist@语校12 小时前
第五十五篇|从解释约束到结构化认知:京都国际学院的语言学校Prompt工程化实践
大数据·数据库·人工智能·python·百度·prompt·知识图谱
计算机毕业编程指导师13 小时前
【Python大数据选题】基于Spark+Django的电影评分人气数据可视化分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·计算机·spark·django·电影评分人气
TDengine (老段)13 小时前
使用 deepseek 快速搭建 TDengine IDMP demo
大数据·数据库·科技·ai·时序数据库·tdengine·涛思数据
Jackyzhe13 小时前
Flink源码阅读:如何生成StreamGraph
大数据·flink
Hello.Reader13 小时前
Flink SQL Window Deduplication按窗口“保留第一条/最后一条”记录(Streaming)
大数据·sql·flink
给朕把屎铲了13 小时前
涛思数据库:DB error: some vnode/qnode/mnode(s) out of service (10.703928s)
大数据·数据库·涛思数据
成长之路51413 小时前
【地理数据】城市居住人口及工作人口分布数据(更新至2023年)
大数据