Merge pull request #169 from flowrean/append-cmd
Add support for FTP append (APPE) command
This commit is contained in:
commit
59f85871cf
@ -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)
|
||||
|
21
conn_test.go
21
conn_test.go
@ -1,9 +1,9 @@
|
||||
package ftp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/textproto"
|
||||
"reflect"
|
||||
@ -19,6 +19,7 @@ type ftpMock struct {
|
||||
proto *textproto.Conn
|
||||
commands []string // list of received commands
|
||||
rest int
|
||||
fileCont *bytes.Buffer
|
||||
dataConn *mockDataConn
|
||||
sync.WaitGroup
|
||||
}
|
||||
@ -135,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")
|
||||
@ -166,7 +174,7 @@ func (mock *ftpMock) listen(t *testing.T) {
|
||||
|
||||
mock.dataConn.Wait()
|
||||
mock.proto.Writer.PrintfLine("150 Opening ASCII mode data connection for file list")
|
||||
mock.dataConn.conn.Write([]byte(testData[mock.rest:]))
|
||||
mock.dataConn.conn.Write(mock.fileCont.Bytes()[mock.rest:])
|
||||
mock.rest = 0
|
||||
mock.proto.Writer.PrintfLine("226 Transfer complete")
|
||||
mock.closeDataConn()
|
||||
@ -268,9 +276,12 @@ func (mock *ftpMock) listenDataConn() (int64, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (mock *ftpMock) recvDataConn() {
|
||||
func (mock *ftpMock) recvDataConn(append bool) {
|
||||
mock.dataConn.Wait()
|
||||
io.Copy(ioutil.Discard, mock.dataConn.conn)
|
||||
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
21
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)
|
||||
|
Loading…
Reference in New Issue
Block a user