- // Folder Download flow:
- // 1. Get filePath from the transfer
- // 2. Iterate over files
- // 3. For each file:
- // Send file header to client
- // The client can reply in 3 ways:
- //
- // 1. If type is an odd number (unknown type?), or file download for the current file is completed:
- // client sends []byte{0x00, 0x03} to tell the server to continue to the next file
- //
- // 2. If download of a file is to be resumed:
- // client sends:
- // []byte{0x00, 0x02} // download folder action
- // [2]byte // Resume data size
- // []byte file resume data (see myField_FileResumeData)
- //
- // 3. Otherwise download of the file is requested and client sends []byte{0x00, 0x01}
- //
- // When download is requested (case 2 or 3), server replies with:
- // [4]byte - file size
- // []byte - Flattened File Object
- //
- // After every file download, client could request next file with:
- // []byte{0x00, 0x03}
- //
- // This notifies the server to send the next item header
-
- var fh FilePath
- _ = fh.UnmarshalBinary(fileTransfer.FilePath)
- fullFilePath := fmt.Sprintf("%v/%v", s.Config.FileRoot+fh.String(), string(fileTransfer.FileName))
-
- basePathLen := len(fullFilePath)
-
- readBuffer := make([]byte, 1024)
-
- s.Logger.Infow("Start folder download", "path", fullFilePath, "ReferenceNumber", fileTransfer.ReferenceNumber, "RemoteAddr", conn.RemoteAddr())
-
- i := 0
- _ = filepath.Walk(fullFilePath+"/", func(path string, info os.FileInfo, _ error) error {
- i += 1
- subPath := path[basePathLen:]
- s.Logger.Infow("Sending fileheader", "i", i, "path", path, "fullFilePath", fullFilePath, "subPath", subPath, "IsDir", info.IsDir())
-
- fileHeader := NewFileHeader(subPath, info.IsDir())
-
- if i == 1 {
- return nil
- }
-
- // Send the file header to client
- if _, err := conn.Write(fileHeader.Payload()); err != nil {
- s.Logger.Errorf("error sending file header: %v", err)
- return err
- }
-
- // Read the client's Next Action request
- //TODO: Remove hardcoded behavior and switch behaviors based on the next action send
- if _, err := conn.Read(readBuffer); err != nil {
- return err
- }
-
- s.Logger.Infow("Client folder download action", "action", fmt.Sprintf("%X", readBuffer[0:2]))
-
- if info.IsDir() {
- return nil
- }
-
- splitPath := strings.Split(path, "/")
-
- ffo, err := NewFlattenedFileObject(strings.Join(splitPath[:len(splitPath)-1], "/"), info.Name())
- if err != nil {
- return err
- }
- s.Logger.Infow("File download started",
- "fileName", info.Name(),
- "transactionRef", fileTransfer.ReferenceNumber,
- "RemoteAddr", conn.RemoteAddr().String(),
- "TransferSize", fmt.Sprintf("%x", ffo.TransferSize()),
- )
-
- // Send file size to client
- if _, err := conn.Write(ffo.TransferSize()); err != nil {
- s.Logger.Error(err)
- return err
- }
-
- // Send file bytes to client
- if _, err := conn.Write(ffo.BinaryMarshal()); err != nil {
- s.Logger.Error(err)
- return err
- }
-
- file, err := os.Open(path)
- if err != nil {
- return err
- }
-
- sendBuffer := make([]byte, 1048576)
- totalBytesSent := len(ffo.BinaryMarshal())
-
- for {
- bytesRead, err := file.Read(sendBuffer)
- if err == io.EOF {
- // Read the client's Next Action request
- //TODO: Remove hardcoded behavior and switch behaviors based on the next action send
- if _, err := conn.Read(readBuffer); err != nil {
- s.Logger.Errorf("error reading next action: %v", err)
- return err
- }
- break
- }