如果单个 XML 文件符合要求,如何创建多个 XML 文件?
How to create multiple XML files if single XML file matches the requirements?
下面是我的客户端代码,它从 golang grpc
服务器流式传输所有客户 url 并且工作正常。它采用 Request
输入参数并在特定 clientId
的基础上流式传输 customer url's
。在我下面的代码中,我正在为 ClientId 12345
流式传输所有客户 url 并且它工作正常。
我还创建了一个 XML 文件,其中包含所有 URL,特别是 clientId
,如下所示。例如:下面将创建 12345_abc.xml
XML 文件,其中包含特定格式的所有 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
class -
// 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
文件不应超过 50K
URL 的最大值
我知道 50k URL 限制会比 50MB 限制更快达到,但这就是我的要求。现在根据以上逻辑,我需要为特定的 clientId
创建多个 XML 文件。所有这些多个文件都可以像这样 12345_abc_1.xml
、12345_abc_2.xml
或任何其他更好的命名格式。我有点困惑我应该如何继续这样做。
我可以通过使用 for 循环为 50K
url 添加逻辑,但混淆了大小逻辑,而且我想为每个 clientId
制作这个通用的,所以我遇到了困难这样做。
在你的 WriteTo
函数中,你应该调用类似 w.Write(myBytes)
.
的东西
该函数中 myBytes
的大小就是您要查找的大小。您可以使用 len(myBytes)
或 w.Write(myBytes)
的第一个 return 来获取它。这很重要,因为除了直接计算您将写入的信息外,无法“估计”文件的大小。
您正在将 UrlMap
转换为 WriteTo
函数中某处的字节。这意味着您可以对任何 URL
变量执行相同的操作。
我解决这个问题的方法是有一个 sizeCounter
并添加每次我在 for {
中创建一个新的 URL
变量时将存储的字节数环形。在同一个地方,我还会计算创建的 URL
的数量。有了两个计数器,剩下的就很简单了。
我会在 .Add
函数和 return 函数中添加从 URL
到 bytes
的转换,这样一切都更容易理解。您将不得不将一些变量移动到 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.
下面是我的客户端代码,它从 golang grpc
服务器流式传输所有客户 url 并且工作正常。它采用 Request
输入参数并在特定 clientId
的基础上流式传输 customer url's
。在我下面的代码中,我正在为 ClientId 12345
流式传输所有客户 url 并且它工作正常。
我还创建了一个 XML 文件,其中包含所有 URL,特别是 clientId
,如下所示。例如:下面将创建 12345_abc.xml
XML 文件,其中包含特定格式的所有 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
class -
// 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
文件不应超过50K
URL 的最大值
我知道 50k URL 限制会比 50MB 限制更快达到,但这就是我的要求。现在根据以上逻辑,我需要为特定的 clientId
创建多个 XML 文件。所有这些多个文件都可以像这样 12345_abc_1.xml
、12345_abc_2.xml
或任何其他更好的命名格式。我有点困惑我应该如何继续这样做。
我可以通过使用 for 循环为 50K
url 添加逻辑,但混淆了大小逻辑,而且我想为每个 clientId
制作这个通用的,所以我遇到了困难这样做。
在你的 WriteTo
函数中,你应该调用类似 w.Write(myBytes)
.
该函数中 myBytes
的大小就是您要查找的大小。您可以使用 len(myBytes)
或 w.Write(myBytes)
的第一个 return 来获取它。这很重要,因为除了直接计算您将写入的信息外,无法“估计”文件的大小。
您正在将 UrlMap
转换为 WriteTo
函数中某处的字节。这意味着您可以对任何 URL
变量执行相同的操作。
我解决这个问题的方法是有一个 sizeCounter
并添加每次我在 for {
中创建一个新的 URL
变量时将存储的字节数环形。在同一个地方,我还会计算创建的 URL
的数量。有了两个计数器,剩下的就很简单了。
我会在 .Add
函数和 return 函数中添加从 URL
到 bytes
的转换,这样一切都更容易理解。您将不得不将一些变量移动到 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.