Browse Source

Add support for append (APPE) command

Florian Goetghebeur 5 năm trước cách đây
mục cha
commit
827e50c0bd
3 tập tin đã thay đổi với 54 bổ sung3 xóa
  1. 21 0
      client_test.go
  2. 12 3
      conn_test.go
  3. 21 0
      ftp.go

+ 21 - 0
client_test.go

@@ -113,6 +113,27 @@ func testConn(t *testing.T, disableEPSV bool) {
 		r.Close()
 	}
 
+	data2 := bytes.NewBufferString(testData)
+	err = c.Append("tset", data2)
+	if err != nil {
+		t.Error(err)
+	}
+
+	// Read without deadline, after append
+	r, err = c.Retr("tset")
+	if err != nil {
+		t.Error(err)
+	} else {
+		buf, err := ioutil.ReadAll(r)
+		if err != nil {
+			t.Error(err)
+		}
+		if string(buf) != testData+testData {
+			t.Errorf("'%s'", buf)
+		}
+		r.Close()
+	}
+
 	fileSize, err := c.FileSize("magic-file")
 	if err != nil {
 		t.Error(err)

+ 12 - 3
conn_test.go

@@ -136,7 +136,14 @@ func (mock *ftpMock) listen(t *testing.T) {
 				break
 			}
 			mock.proto.Writer.PrintfLine("150 please send")
-			mock.recvDataConn()
+			mock.recvDataConn(false)
+		case "APPE":
+			if mock.dataConn == nil {
+				mock.proto.Writer.PrintfLine("425 Unable to build data connection: Connection refused")
+				break
+			}
+			mock.proto.Writer.PrintfLine("150 please send")
+			mock.recvDataConn(true)
 		case "LIST":
 			if mock.dataConn == nil {
 				mock.proto.Writer.PrintfLine("425 Unable to build data connection: Connection refused")
@@ -269,9 +276,11 @@ func (mock *ftpMock) listenDataConn() (int64, error) {
 	return p, nil
 }
 
-func (mock *ftpMock) recvDataConn() {
+func (mock *ftpMock) recvDataConn(append bool) {
 	mock.dataConn.Wait()
-	mock.fileCont = new(bytes.Buffer)
+	if !append {
+		mock.fileCont = new(bytes.Buffer)
+	}
 	io.Copy(mock.fileCont, mock.dataConn.conn)
 	mock.proto.Writer.PrintfLine("226 Transfer Complete")
 	mock.closeDataConn()

+ 21 - 0
ftp.go

@@ -629,6 +629,27 @@ func (c *ServerConn) StorFrom(path string, r io.Reader, offset uint64) error {
 	return err
 }
 
+// Append issues a APPE FTP command to store a file to the remote FTP server.
+// If a file already exists with the given path, then the content of the
+// io.Reader is appended. Otherwise, a new file is created with that content.
+//
+// Hint: io.Pipe() can be used if an io.Writer is required.
+func (c *ServerConn) Append(path string, r io.Reader) error {
+	conn, err := c.cmdDataConnFrom(0, "APPE %s", path)
+	if err != nil {
+		return err
+	}
+
+	_, err = io.Copy(conn, r)
+	conn.Close()
+	if err != nil {
+		return err
+	}
+
+	_, _, err = c.conn.ReadResponse(StatusClosingDataConnection)
+	return err
+}
+
 // Rename renames a file on the remote FTP server.
 func (c *ServerConn) Rename(from, to string) error {
 	_, _, err := c.cmd(StatusRequestFilePending, "RNFR %s", from)