Add support for append (APPE) command

This commit is contained in:
Florian Goetghebeur 2020-03-10 11:43:17 +01:00
parent adfb8ed9f3
commit 827e50c0bd
3 changed files with 54 additions and 3 deletions

View File

@ -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)

View File

@ -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
ftp.go
View File

@ -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)