liveBook · Manning

End-of-file indicator

As explained in Go in Practice, Second Edition

End-of-file is the condition indicating that no more data can be read from a file or stream.

An end-of-file indicator signals that no more data can be read from a file or stream. In Go, io.EOF commonly marks normal completion: a reader returns it when iteration reaches the end, so the loop can stop rather than treat the condition as a failure. section 10.2.6

Distinguish the end-of-input signal from other read outcomes:

  • Handle io.EOF as the expected stopping condition when a reader has no more parts or bytes to provide. section 10.2.6
  • Handle an unexpected end separately with io.ErrUnexpectedEOF when incomplete input is an error rather than a normal boundary. section 7.1.1
  • Choose sequential reading for large or streamed data instead of loading an entire file into memory with os.ReadFile. section 7.1.1

Stopping a multipart parser

A multipart upload handler repeatedly calls mr.NextPart(). When the request contains no more parts, the call returns io.EOF; the handler breaks from the loop and proceeds without treating normal completion as an upload error. section 10.2.6

Listing 10.15 Saving uploaded files incrementally
func fileForm(w http.ResponseWriter, r *http.Request) {   #1
     if r.Method == "GET" {                                #2
            t, _ := 
            template.ParseFiles("file_plus.html")         
            t.Execute(w, nil)                             
     } else {
            mr, err := r.MultipartReader()               #3
            if err != nil {                             
                   panic("Failed to read multipart 
                   message")                            
            }                                           





            values := make(map[string][]string)   #4
            maxValueBytes := int64(10 << 20)     #5
            for {   #6
                   part, err := mr.NextPart()   #7
                   if err == io.EOF {           #7
                           break                #7
                   }                            #7
                   name := part.FormName()   #8
                   if name == "" {           #8
                           continue          #8
                   }                         #8
                   filename := part.FileName()   #9
                   var b bytes.Buffer     #10
                   if filename == "" {    #11
                           n, err := io.CopyN(&b, part, 
                           maxValueBytes)   #12
                           if err != nil && err != 
                           io.EOF {                        #13
                                  fmt.Fprint(w, "Error
                                   processing form")   
                                  return               
                           }                           
                           maxValueBytes -= n           #14
                           if maxValueBytes == 0 {     
                                  msg := "multipart 
                                  message too large"     
                                  fmt.Fprint(w, msg)   
                                  return               
                          }                            
                          values[name] = append(values
                          [name],b.String())   #15
                          continue             #15
                   }
                   dst, err := os.Create("/tmp/" + 
                   filename)                                  #16
                   defer dst.Close()                   #17
                   if err != nil {
                          return
                   }
                   for { #                                 #18
                           buffer := make([]byte, 100000) 
                           cBytes, err := 
                           part.Read(buffer)              
                           if err == io.EOF {             
                                  break                   
                           }                              
                           dst.Write(buffer[0:cBytes])    
                   }                                      
            }
            fmt.Fprint(w, "Upload complete")
     }
}

Inspect the complete loop that retrieves multipart parts, tests for io.EOF, and exits when the request body is exhausted. section 10.2.6

How Go in Practice, Second Edition covers it

How deep it goes
Explains normal io.EOF termination and contrasts it with unexpected EOF, but does not develop reader internals.
Examples
Uses sequential file reads, line scanning, JSON-stream processing, and multipart upload iteration.
What it assumes
Assumes basic Go error handling and file or request reading while demonstrating reader-based processing from standard-library APIs.
What you can do after
Diagnose normal input exhaustion versus incomplete input while implementing reader and multipart-processing loops.
How it connects
Connects file reading, io.Reader, os.Open, bufio.Scanner, and multipart parsing.

Also explained in

Compare how 2 books explain end-of-file indicator →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Go in Practice, Second Edition. The section links point to the parts of the book it draws on.