如果单个 XML 文件符合要求,如何创建多个 XML 文件?



下面是我的客户端代码,它从服务器流式传输所有客户golang grpc网址,并且工作正常。它采用Request输入参数,并基于特定clientIdcustomer url's流式传输。 在下面的代码中,我正在流式传输所有客户 url 以供ClientId 12345并且工作正常。

我还创建了一个XML文件,其中包含所有URL用于特定clientId,如下所示。例如:下面将创建12345_abc.xmlXML文件,其中包含特定格式的所有URL。

func main() {
// this "clientId" will be configurable in future
clientId := 12345
timeout := time.Duration(1000) * time.Millisecond
ctx, _ := context.WithTimeout(context.Background(), timeout)
conn, err := grpc.DialContext(ctx, "localhost:50005", grpc.WithInsecure())
if err != nil {
log.Fatalf("can not connect with server %v", err)
}
// create stream
client := pb.NewCustomerServiceClient(conn)
req := &pb.Request{ClientId: clientId}
stream, err := client.FetchResponse(context.Background(), req)
if err != nil {
log.Fatalf("open stream error %v", err)
}
// create new object to populate all URL data in memory
urlHolder := NewClient()
t := time.Unix(0, 0).UTC()
done := make(chan bool)
go func() {
for {
resp, err := stream.Recv()
if err == io.EOF {
done <- true
return
}
if err != nil {
log.Fatalf("can not receive %v", err)
}
log.Printf("Resp received: %s", resp.GetCustomerUrl())
// populate URL object with all the required field in it
urlHolder.Add(&URL{
Loc:        resp.GetCustomerUrl(),
LastMod:    &t,
ChangeFreq: Daily,
Priority:   10.2,
})
}
}()
<-done
log.Printf("finished")
// create an XML file with all the URL's in it and then save it on disk
// for particular clientId. This will create "12345_abc.xml"
file, _ := os.Create(fmt.Sprintf("%d_abc.xml", clientId))
urlHolder.WriteTo(file)
}

这是我urlholder.go文件:

type URL struct {
Loc        string     `xml:"loc"`
LastMod    *time.Time `xml:"lastmod"`
ChangeFreq ChangeFreq `xml:"changefreq"`
Priority   float32    `xml:"priority"`
}
type UrlMap struct {
XMLName xml.Name `xml:"urlset"`
Xmlns   string   `xml:"xmlns,attr"`
URLs    []*URL   `xml:"url"`
Minify  bool     `xml:"-"`
}
func NewClient() *UrlMap {
return &UrlMap{
Xmlns: "http://www.sitemaps.org/schemas/sitemap/0.9",
URLs:  make([]*URL, 0),
}
}
func (s *UrlMap) Add(u *URL) {
s.URLs = append(s.URLs, u)
}
// WriteTo writes XML encoded urlMap to given io.Writer.
func (s *UrlMap) WriteTo(w io.Writer) (n int64, err error) {
cw := NewCounterWriter(w)
_, err = cw.Write([]byte(xml.Header))
if err != nil {
return cw.Count(), err
}
en := xml.NewEncoder(cw)
if !s.Minify {
en.Indent("", "  ")
}
err = en.Encode(s)
cw.Write([]byte{'n'})
return cw.Count(), err
}

这是我CounterWriter课——

// CounterWriter implements io.Writer. Count of bytes written is tracked.
type CounterWriter struct {
writer io.Writer
count  int64
}
var _ io.Writer = (*CounterWriter)(nil)
// NewCounterWriter wraps io.Writer and returns CounterWriter.
func NewCounterWriter(w io.Writer) (cw *CounterWriter) {
return &CounterWriter{
writer: w,
}
}
// Write calls Write on the wrapped io.Writer and adds the number of bytes
// written to the counter.
func (cw *CounterWriter) Write(p []byte) (n int, err error) {
n, err = cw.writer.Write(p)
cw.count = cw.count + int64(n)
return n, err
}
// Count returns the number of bytes written to the Writer.
func (cw *CounterWriter) Count() (n int64) {
return cw.count
}

问题陈述

上面的代码工作正常,但如果它符合以下要求,我需要将一个XML文件拆分为多个XML文件以获得相同的clientId

  • 单个XML文件的最大50MB。它可以是近似的,不必是准确的。
  • 单个XML文件的 URL 最大值不应超过50K

我知道50k URL限制会比50MB限制更早达到50m限制很奇怪,但这就是我得到的要求。现在基于上述逻辑,我需要为特定clientId制作多个XML文件。所有这些多个文件都可以像这样12345_abc_1.xml12345_abc_2.xml或任何其他更好的命名格式。我对应该如何继续这样做有点困惑。

我可以通过使用 for 循环为 url 添加逻辑50K但对大小逻辑感到困惑,而且我想为每个clientId制作这个通用,所以我在这样做时遇到了困难。

在你的WriteTo函数中,你应该调用类似w.Write(myBytes)的东西。

该函数中myBytes的大小就是您要查找的大小。您可以使用len(myBytes)或第一次返回w.Write(myBytes)来获取它。这很重要,因为除了直接计算您将写入的信息外,没有办法"估计"文件的大小。

您正在将UrlMap转换为WriteTo函数中的某个位置的字节。这意味着您可以对任何URL变量执行相同的操作。

我解决这个问题的方法是有一个sizeCounter,并添加每次在for {循环中创建新的URL变量时存储的字节数。在同一个地方,我还会计算创建的URL数量。使用这两个计数器,其余的都很容易。

我会在.Add函数中添加从URLbytes的转换并返回它,以便一切更容易理解。您将不得不将一些变量移动到 go 例程中。


func (s *UrlMap) Add(u *URL) (int) { // Modify this function to count the size and return it
s.URLs = append(s.URLs, u)
var urlBytes []byte
var err error
urlBytes, err = xml.Marshal(u) // Transform to bytes using xml.Marshal or xml.MarshalIndent
if err != nil {
panic(err) // or return the error if you want
}
return len(urlBytes)
}
t := time.Unix(0, 0).UTC()
done := make(chan bool)
go func() {
// create new object to populate all URL data in memory
urlHolder := NewClient()
urlCounter := 0
byteCounter := 0
fileCounter := 0
for {
resp, err := stream.Recv()
if err == io.EOF {
done <- true
file, _ := os.Create(fmt.Sprintf("%d_abc_%d.xml", clientId, fileCounter))
urlHolder.WriteTo(file)
return
}
if err != nil {
log.Fatalf("can not receive %v", err)
}
log.Printf("Resp received: %s", resp.GetCustomerUrl())
// I add the bytes of the URL here as a return
urlBytes := urlHolder.Add(&URL{
Loc:        resp.GetCustomerUrl(),
LastMod:    &t,
ChangeFreq: Daily,
Priority:   10.2,
})
byteCounter += urlBytes
urlCounter += 1
if byteCounter > 49000000 || urlCounter >= 50000 { 
file, _ := os.Create(fmt.Sprintf("%d_abc_%d.xml", clientId, fileCounter))
urlHolder.WriteTo(file)
urlHolder = NewClient() // create a new object for next loop
fileCounter += 1 // prepare fileCounter for next loop
byteCounter = 0 // restart count variables
urlCounter = 0
}
}
}()
<-done
log.Printf("finished")
// No longer write the files here.

最新更新