mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 03:02:14 +01:00 
			
		
		
		
	Refactor markup/csv: don't read all to memory (#29760)
This commit is contained in:
		
							parent
							
								
									bbef5fc5c3
								
							
						
					
					
						commit
						e79a807a84
					
				| @ -77,29 +77,62 @@ func writeField(w io.Writer, element, class, field string) error { | ||||
| } | ||||
| 
 | ||||
| // Render implements markup.Renderer | ||||
| func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { | ||||
| func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { | ||||
| 	tmpBlock := bufio.NewWriter(output) | ||||
| 	maxSize := setting.UI.CSV.MaxFileSize | ||||
| 
 | ||||
| 	// FIXME: don't read all to memory | ||||
| 	rawBytes, err := io.ReadAll(input) | ||||
| 	if maxSize == 0 { | ||||
| 		return r.tableRender(ctx, input, tmpBlock) | ||||
| 	} | ||||
| 
 | ||||
| 	rawBytes, err := io.ReadAll(io.LimitReader(input, maxSize+1)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < int64(len(rawBytes)) { | ||||
| 		if _, err := tmpBlock.WriteString("<pre>"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if _, err := tmpBlock.WriteString(html.EscapeString(string(rawBytes))); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if _, err := tmpBlock.WriteString("</pre>"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return tmpBlock.Flush() | ||||
| 	if int64(len(rawBytes)) <= maxSize { | ||||
| 		return r.tableRender(ctx, bytes.NewReader(rawBytes), tmpBlock) | ||||
| 	} | ||||
| 	return r.fallbackRender(io.MultiReader(bytes.NewReader(rawBytes), input), tmpBlock) | ||||
| } | ||||
| 
 | ||||
| func (Renderer) fallbackRender(input io.Reader, tmpBlock *bufio.Writer) error { | ||||
| 	_, err := tmpBlock.WriteString("<pre>") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, bytes.NewReader(rawBytes)) | ||||
| 	scan := bufio.NewScanner(input) | ||||
| 	scan.Split(bufio.ScanRunes) | ||||
| 	for scan.Scan() { | ||||
| 		switch scan.Text() { | ||||
| 		case `&`: | ||||
| 			_, err = tmpBlock.WriteString("&") | ||||
| 		case `'`: | ||||
| 			_, err = tmpBlock.WriteString("'") // "'" is shorter than "'" and apos was not in HTML until HTML5. | ||||
| 		case `<`: | ||||
| 			_, err = tmpBlock.WriteString("<") | ||||
| 		case `>`: | ||||
| 			_, err = tmpBlock.WriteString(">") | ||||
| 		case `"`: | ||||
| 			_, err = tmpBlock.WriteString(""") // """ is shorter than """. | ||||
| 		default: | ||||
| 			_, err = tmpBlock.Write(scan.Bytes()) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = tmpBlock.WriteString("</pre>") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return tmpBlock.Flush() | ||||
| } | ||||
| 
 | ||||
| func (Renderer) tableRender(ctx *markup.RenderContext, input io.Reader, tmpBlock *bufio.Writer) error { | ||||
| 	rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, input) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @ -4,6 +4,8 @@ | ||||
| package markup | ||||
| 
 | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 
 | ||||
| @ -29,4 +31,12 @@ func TestRenderCSV(t *testing.T) { | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.EqualValues(t, v, buf.String()) | ||||
| 	} | ||||
| 
 | ||||
| 	t.Run("fallbackRender", func(t *testing.T) { | ||||
| 		var buf bytes.Buffer | ||||
| 		err := render.fallbackRender(strings.NewReader("1,<a>\n2,<b>"), bufio.NewWriter(&buf)) | ||||
| 		assert.NoError(t, err) | ||||
| 		want := "<pre>1,<a>\n2,<b></pre>" | ||||
| 		assert.Equal(t, want, buf.String()) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user