C# FTP上传代码

using System;
using System.IO;
using System.Net.Sockets;

namespace ftpClientUtil
{
    internal class FtpWebStream : Stream
    {
        private FtpWebResponse response;
        private NetworkStream dataStream;

        public FtpWebStream(NetworkStream dataStream, FtpWebResponse response)
        {
            this.dataStream = dataStream;
            this.response = response;
        }

        public override void Close()
        {
            response.Close();
            base.Close();
        }

        public override void Flush()
        {
            dataStream.Flush();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return dataStream.Read(buffer, offset, count);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException("Seek not supported.");
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException("SetLength not supported.");
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            dataStream.Write(buffer, offset, count);
        }

        public override bool CanRead
        {
            get { return dataStream.CanRead; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return dataStream.CanWrite; }
        }

        public override long Length
        {
            get { throw new NotSupportedException("Length not supported."); }
        }

        public override long Position
        {
            get
            {
                throw new NotSupportedException("Position not supported.");
            }
            set
            {
                throw new NotSupportedException("Position not supported.");
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;

namespace ftpClientUtil
{
    public class FtpWebRequest : WebRequest
    {
        private string username = "anonymous";
        internal string password = "someuser@someemail.com";

        private Uri uri;
        private bool binaryMode = true;
        private string method = "Get";

        internal FtpWebRequest(Uri uri)
        {
            this.uri = uri;
        }

        public string Username
        {
            get { return username; }
            set { username = value; }
        }

        public string Password
        {
            get { return password; }
            set { password = value; }
        }

        public bool BinaryMode
        {
            get { return binaryMode; }
            set { binaryMode = value; }
        }

        public override System.Uri RequestUri
        {
            get
            {
                return uri;
            }
        }

        public override string Method
        {
            get
            {
                return method;
            }
            set
            {
                method = value;
            }
        }

        public override WebResponse GetResponse()
        {
            FtpWebResponse response = new FtpWebResponse(this);
            return response;
        }
    }
}
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace ftpClientUtil
{
    public class FtpWebResponse : WebResponse
    {
        private FtpWebRequest request;
        private FtpClient client;

        internal FtpWebResponse(FtpWebRequest request)
        {
            this.request = request;
        }

        public override Stream GetResponseStream()
        {
            string hostname;
            string filename;
            GetUriComponents(request.RequestUri.ToString(), out hostname, out filename);

            //Connect to the Ftp server and get a stream
            client = new FtpClient(request.Username, request.password);
            client.Connect(hostname);

            NetworkStream dataStream = null;
            switch (request.Method)
            {
                case "Get":
                case "RETR":
                    dataStream = client.GetReadStram(filename, request.BinaryMode);
                    break;
                case "PUT":
                case "STOR":
                    dataStream = client.GetWriteStream(filename, request.BinaryMode);
                    break;
                default:
                    throw new WebException("Method " + request.Method + " not supported");
            }

            //create and return an FtpWebStream (to close the underlying objects)
            FtpWebStream ftpStream = new FtpWebStream(dataStream, this);
            return ftpStream;
        }

        private void GetUriComponents(string uri, out string hostname, out string fileName)
        {
            //Check that URI has at least 7 characters, or we'll get an error
            uri = uri.ToLower();
            if (uri.Length < 7)
                throw new UriFormatException("Invalid URI");

            //Check that URI starts "ftp://", and remove that from the start
            if (uri.Substring(0,6)!="ftp://")
                throw new NotSupportedException("Only FTP requests are supported");
            else
                uri=uri.Substring(6,uri.Length-6);

            //Divide the rest of the URI into the hostname and the filename
            string[] uriParts = uri.Split(new char[]{'/'},2);
            if (uriParts.Length != 2)
                throw new UriFormatException("Invalid URI");

            hostname = uriParts[0];
            fileName = uriParts[1];
        }

        public override void Close()
        {
            client.Close();
        }
    }
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;

namespace ftpClientUtil
{
    public class FtpRequestCreator : IWebRequestCreate
    {
        public FtpRequestCreator()
        { }

        public System.Net.WebRequest Create(System.Uri uri)
        {
            return new FtpWebRequest(uri);
        }
    }
}
using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;

namespace ftpClientUtil
{
    internal sealed class FtpClient
    {
        private const int bufferSize = 65536;
        private NetworkStream controlStream;
        private NetworkStream dataStream;
        private TcpClient client;
        private string username;
        private string password;
        private TcpClient dataClient = null;

        public FtpClient(string username, string password)
        {
            this.username = username;
            this.password = password;
        }

        public void Connect(string hostname)
        {
            //Set the private fields representing the TCP control connection to
            //the server and the NetworkStream used to communicate with the server
            client = new TcpClient(hostname, 21);
            controlStream = client.GetStream();

            string responseMessage;
            if (GetResponse(out responseMessage) != 220)
            {
                throw new WebException(responseMessage);
            }
            Logon(username, password);
        }

        public int GetResponse(out string responseMessage)
        {
            //Read the response from the server, trim any nulls, and return it.
            byte[] response = new byte[client.ReceiveBufferSize];
            controlStream.Read(response, 0, response.Length);
            responseMessage = Encoding.ASCII.GetString(response).Replace("/0", "");
            return int.Parse(responseMessage.Substring(0, 3));
        }

        private void Logon(string username, string password)
        {
            //Send a USER FTP commnad. The server should repond with a 331 message to ask for the user's password
            string respMessage;
            int resp = SendCommand("USER " + username, out respMessage);

            if (resp != 331 && resp != 230)
                throw new UnauthorizedAccessException("Unable to login to the FTP server");

            if (resp != 230)
            {
                //Send a PASS FTP command. The server should respond with a 230 message to say that the user is now logged in.
                resp = SendCommand("PASS " + password, out respMessage);
                if (resp != 230)
                    throw new UnauthorizedAccessException("FTP server can't authenticate username");
            }
        }

        public NetworkStream GetReadStram(string filename, bool binaryMode)
        {
            return DownloadFile(filename, binaryMode);
        }

        public NetworkStream GetWriteStream(string filename, bool binaryMode)
        {
            return UploadFile(filename, binaryMode);
        }

        public NetworkStream DownloadFile(string filename, bool binaryMode)
        {
            if (dataClient == null)
                dataClient = CreateDataSocket();

            SetBinaryMode(binaryMode);
            string respMessage;
            int resp = SendCommand("RETR " + filename, out respMessage);

            if (resp != 150 && resp != 125)
                throw new WebException(respMessage);

            dataStream = dataClient.GetStream();
            return dataStream;
        }

        private NetworkStream UploadFile(string filename, bool binaryMode)
        {
            if (dataClient == null)
                dataClient = this.CreateDataSocket();

            //Set binary or ASCII mode
            SetBinaryMode(binaryMode);

            //Send a STOR command to say we want to upload a file
            string respMessage;
            int resp = SendCommand("STOR " + filename, out respMessage);

            //we should get a 150 resposne to say that the server is opening the data connection
            if (resp != 150 && resp != 125)
                throw new WebException("Can't upload file to the server");

            dataStream = dataClient.GetStream();
            return dataStream;
        }

        private void SetBinaryMode(bool binaryMode)
        {
            int resp;
            string respMessage;
            if (binaryMode)
                resp = SendCommand("TYPE I", out respMessage);
            else
                resp = SendCommand("TYPE A", out respMessage);

            if (resp != 200)
                throw new WebException(respMessage);
        }

        private TcpClient CreateDataSocket()
        {
            //request server to listen on a data port (not the default data port) and wait for a connection
            string respMessage;
            int resp = SendCommand("PASV", out respMessage);
            if (resp != 227)
                throw new WebException(respMessage);

            //The response includes the host address and port numner
            //IP address and port numner separated with ',' Create the IP address and port number
            int[] parts = new int[6];
            try
            {
                int index1 = respMessage.IndexOf('(');
                int index2 = respMessage.IndexOf(')');
                string endPointData = respMessage.Substring(index1+1,index2-index1-1);

                string [] endPointParts = endPointData.Split(',');

                for (int i=0;i<6;i++)
                    parts[i] = int.Parse(endPointParts[i]);
            }
            catch
            {
                throw new WebException("Malformed PASV reply: " + respMessage);
            }

            string ipAddress = parts[0] + "." + parts[1] + "." + parts[2] + "." + parts[3];
            int port = (parts[4] << 8) + parts[5];

            //Create a client socket
            TcpClient dataClient = new TcpClient();

            //Connect to the data poer of the server
            try {
                IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ipAddress),port);
                dataClient.Connect(remoteEP);
            }
            catch (Exception)
            {
                throw new WebException("Can't connected to remote server");
            }
            return dataClient;
        }

        public void Close()
        {
            if (dataStream != null)
            {
                dataStream.Close();
                dataStream = null;
            }
           
            string respMessage;
            GetResponse(out respMessage);

            Logoff();

            //Close the control TcpClient and NetworkStream
            controlStream.Close();
            client.Close();
        }

        public void Logoff()
        {
            //Send the QUIT command to log off from the server
            string respMessage;
            SendCommand("STAT", out respMessage);   //Test only
            GetResponse(out respMessage);           //STAT has 2 response lines!

            SendCommand("QUIT", out respMessage);
        }

        internal int SendCommand(string command, out string respMessage)
        {
            //Convert the command string (terminated with a CRLF) into a byte array, and write it to control stream.
            byte[] request = Encoding.ASCII.GetBytes(command + "/r/n");
            controlStream.Write(request, 0, request.Length);
            return GetResponse(out respMessage);
        }
    }
}
以下是客户端的调用:
using System;
using System.IO;
using System.Net;
using ftpClientUtil;

namespace ConsoleApplication1
{
    class Program
    {
        const int bufferSize = 65536;
        static void Main(string[] args)
        {
            //Register the ftp schema.
            //Alternatively, a config file could be used
            WebRequest.RegisterPrefix("ftp", new FtpRequestCreator());

            //UploadDemo();
        }

        //Upload a file using FtpWebRequest
        public static void UploadDemo()
        {
            ftpClientUtil.FtpWebRequest req = (ftpClientUtil.FtpWebRequest)WebRequest.Create("ftp://192.168.0.1/demofile.bmp");
            req.Username = "Administrator";
            req.Password = "secret";
            req.Method = "PUT"; //STOR or PUT
            req.BinaryMode = true;
            Stream writeStream = req.GetResponse().GetResponseStream();

            FileStream fs = new FileStream(@"c:/temp/cool.bmp", FileMode.Open);

            byte[] buffer = new byte[bufferSize];
            int read;
            while ((read = fs.Read(buffer, 0, bufferSize)) > 0)
                writeStream.Write(buffer, 0, bufferSize);

            writeStream.Close();
            fs.Close();
        }

        //Downlaod a file using FtpWebRequest
        public static void DownloadDemo()
        {
            ftpClientUtil.FtpWebRequest req = (ftpClientUtil.FtpWebRequest)WebRequest.Create("ftp://192.168.0.1/sample.bmp");

            //defualts:
            req.Username = "anonymous";
            req.Password = "someuser@somemail.com";
            req.BinaryMode = true;
            req.Method = "GET";

            ftpClientUtil.FtpWebResponse resp = (ftpClientUtil.FtpWebResponse)req.GetResponse();
            Stream stream = resp.GetResponseStream();

            //Read a binary file
            FileStream fs = new FileStream(@"c:/temp/sample.bmp", FileMode.Create);
            byte[] buffer = new byte[bufferSize];
            int count;
            do
            {
                Array.Clear(buffer, 0, bufferSize);
                count = stream.Read(buffer, 0, bufferSize);
                fs.Write(buffer, 0, count);
            } while (count > 0);

            stream.Close();
            fs.Close();

            //read a text file
            StreamReader reader = new StreamReader(stream);
            string line;
            while ((line = reader.ReadLine()) != null)
                Console.WriteLine(line);

            reader.Close();
        }
    }
}
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace TCPServerDemo
{
    class ClientHandler
    {
        public TcpClient clientSocket;

        public void RunClient()
        {
            //Create the stream classes
            Console.WriteLine(clientSocket.Client.AddressFamily.ToString());
            StreamReader readerStream = new StreamReader(clientSocket.GetStream());
            NetworkStream writestream = clientSocket.GetStream();

            string returnData = readerStream.ReadLine();
            string userName = returnData;

            Console.WriteLine("Welcome " + userName + " to the Server");
            while (true)
            {
                returnData = readerStream.ReadLine();
                if (returnData.IndexOf("QUIT") > -1)
                {
                    Console.WriteLine("Bye Bye " + userName);
                    break;
                }

                Console.WriteLine(userName + ": " + returnData);
                returnData += "/r/n";

                byte[] dataWrite = Encoding.ASCII.GetBytes(returnData);
                writestream.Write(dataWrite, 0, dataWrite.Length);
            }
            clientSocket.Close();
        }
    }
    public class EchoServer
    {
        const int ECHO_PORT = 8080;
        public static int nClient = 0;

        static void Main(string[] args)
        {
            try
            {
                //Bind the Server to local port
                TcpListener clientListener = new TcpListener(IPAddress.Parse("127.0.0.1"),ECHO_PORT);

                //start to listen
                clientListener.Start();

                Console.WriteLine("Waiting for connection...");

                while (true)
                {
                    //Accept the connection
                    TcpClient client = clientListener.AcceptTcpClient();
                    ClientHandler cHandler = new ClientHandler();

                    //pass value to the ClientHandler object
                    cHandler.clientSocket = client;
                    string ipaddressport = client.Client.RemoteEndPoint.ToString();
                    Console.WriteLine(ipaddressport.Substring(0, ipaddressport.IndexOf(":")) + "--------------"+ipaddressport.Substring(ipaddressport.IndexOf(":")+1), ipaddressport.Length - ipaddressport.IndexOf(":")-1);
                    //Create a new thread for the client
                    Thread clientThread = new Thread(new ThreadStart(cHandler.RunClient));
                    clientThread.Start();
                }
                clientListener.Stop();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
    }
}
using System;
using System.Net;
using System.IO;
using System.Net.Sockets;
using System.Text;

namespace TCPClientDemo
{
    class Program
    {
        const int ECHO_PORT = 8080;
        static void Main(string[] args)
        {
            Console.Write("Your UserName:");
            string userName = Console.ReadLine();
            Console.WriteLine("-----Logged in-----");

            try
            {
                //Create a connection with the Chatserver
                TcpClient eClient = new TcpClient("127.0.0.1", ECHO_PORT);

                //Create the stream classes
                StreamReader readerStream = new StreamReader(eClient.GetStream());
                NetworkStream writeStream = eClient.GetStream();

                string dataToSend;

                dataToSend = userName;
                dataToSend += "/r/n";

                //Send username to the server
                byte[] data = Encoding.ASCII.GetBytes(dataToSend);
                writeStream.Write(data, 0, data.Length);

                while (true)
                {
                    Console.WriteLine(userName + ":");
                    //Read line from server
                    dataToSend = Console.ReadLine();
                    dataToSend += "/r/n";
                    data = Encoding.ASCII.GetBytes(dataToSend);
                    writeStream.Write(data, 0, data.Length);
                    //If QUIT is sent, then quit application
                    if (dataToSend.IndexOf("QUIT") > -1)
                        break;

                    string returnData;

                    //Receive response from server
                    returnData = readerStream.ReadLine();
                    Console.WriteLine("Server: " + returnData);
                }

                //Close TcpClient
                eClient.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
    }
}

TCP也就是基于SOCKET传输,上面是简单的例子,难一些的是把文件拆分,下面是一个UDP的文件传输,改改可以用在上面的TCP上
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Xml.Serialization;
using System.Diagnostics;
using System.Threading;

namespace FileSender
{
public class FileSender
{
//File details (Req. for receiver)
[Serializable]
public class FileDetails
{
public string FILETYPE="";
public long FILESIZE=0;
}
private static FileDetails fileDet = new FileDetails();

//UdpClient - related fields
private static IPAddress remoteIPAddress;
private const int remotePort=5002;
private static UdpClient sender = new UdpClient();
private static IPEndPoint endPoint;
//Filestream object
private static FileStream fs;

[STAThread]
static void Main(string[] args)
{
try
{
//Get remote IP address and create IPEndPoint
                //Console.WriteLine("Enter Remote IP address");
                remoteIPAddress = IPAddress.Parse("127.255.255.255");// IPAddress.Parse(Console.ReadLine().ToString());
endPoint = new IPEndPoint(remoteIPAddress,remotePort);

//Get file path. (IMP:file size should be less than 8K)
                Console.WriteLine("Enter file path and name to send.");
                fs = new FileStream(@Console.ReadLine().ToString(), FileMode.Open, FileAccess.Read);

                //if (fs.Length > 8192)
                //{
                //    Console.WriteLine("This version transfers files with size <8192 bytes");
                //    sender.Close();
                //    fs.Close();
                //    return;
                //}

SendFileInfo();
Thread.Sleep(2000);

                //SendFile();
                #region big
                byte[] a = Encoding.ASCII.GetBytes(((char)3).ToString());

                long remainlength = fs.Length - (long)(fs.Length / 8192) * 8192 + 1;
                if (fs.Length > 8192)
                {
                    for (int i = 0; i < fs.Length / 8192; i++)
                    {
                        byte[] fixbyte = new byte[8192];
                        fs.Read(fixbyte, 0, 8192);
                        SendFile(fixbyte);
                        Console.WriteLine("send " + (i + 1).ToString() + " times");
                    }
                    byte[] remainbyte = new byte[remainlength];
                    fs.Read(remainbyte, 0, (int)remainlength - 1);
                    Array.Copy(Encoding.ASCII.GetBytes(((char)3).ToString()), 0, remainbyte, remainbyte.Length - 1, 1);
                    SendFile(remainbyte);
                }
                #endregion

                Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

public static void SendFileInfo()
{
//Get file type or extension
fileDet.FILETYPE = fs.Name.Substring((int)fs.Name.Length -3,3);
//Get file length(futrue purpose)
fileDet.FILESIZE = fs.Length;

XmlSerializer fileSerializer = new XmlSerializer(typeof(FileDetails));
MemoryStream stream = new MemoryStream();
//Serialize object
fileSerializer.Serialize(stream,fileDet);
//Stream to byte
stream.Position = 0;
byte[] bytes = new byte[stream.Length];
stream.Read(bytes,0,Convert.ToInt32(stream.Length));

Console.WriteLine("Sending file details...");
//Send file details
            sender.Send(bytes, bytes.Length, endPoint);
stream.Close();
}

public static void SendFile()
{
//Creating a file stream
byte[] bytes = new byte[fs.Length];
//stream to bytes
fs.Read(bytes,0,bytes.Length);
Console.WriteLine("Sending file...size=" + fs.Length + " bytes");
try
{
sender.Send(bytes,bytes.Length, endPoint);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
finally
{
//Clean up
fs.Close();
sender.Close();
}
Console.Read();
Console.WriteLine("File sent sucessfully.");
}

        public static void SendFile(byte[] subfilebytes)
        {
            try
            {
                sender.Send(subfilebytes, subfilebytes.Length, endPoint);
                //Console.WriteLine(Encoding.ASCII.GetString(subfilebytes));
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            finally
            {
                //sender.Close();
            }
            Console.WriteLine("Sub File Part sent sucessfully.");
        }
}
}
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using System.Text;
using System.Xml.Serialization;

namespace FileReceive
{
public class FileRecv
{
[Serializable]
public class FileDetails
{
public string FILETYPE = "";
public long FILESIZE = 0;
}

private static FileDetails fileDet;
//UdpClient vars
private static int localPort = 5002;
private static UdpClient receivingUdpClient = new UdpClient(localPort);
private static IPEndPoint RemoteIpEndPoint = null;
        private static FileStream fs;
        private static byte[] receiveBytes = new byte[0];
        private static byte[] subreceiveBytes = new byte[0];


[STAThread]
static void Main(string[] args)
{
//Get the file details
GetFileDetails();
//Receive file;
ReceiveFile();
}

private static void GetFileDetails()
{
try
{
Console.WriteLine("-----------------Waiting to get File Details!!----------------");
//Receive File info
                byte[] receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint);
                Console.WriteLine("receive file details!!");

                XmlSerializer fileSerializer = new XmlSerializer(typeof(FileDetails));
                MemoryStream stream1 = new MemoryStream();

                //Received byte to stream
                stream1.Write(receiveBytes, 0, receiveBytes.Length);
                stream1.Position = 0; //IMP

                //call the Deserialize method and cast to the object type
                fileDet = (FileDetails)fileSerializer.Deserialize(stream1);
                Console.WriteLine("Receive file of type." + fileDet.FILETYPE + " whose size is " + fileDet.FILESIZE.ToString() + " bytes");
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

        public static void ReceiveFile()
        {
            int insertpoint=0,i=0;
            byte[] a = new byte[1];
            try
            {
                receiveBytes = new byte[fileDet.FILESIZE];
                Console.WriteLine("--------------Waiting to get File-------------------");

                //Receive file
                //receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint);
                //Console.WriteLine("------File received...Saving....");

                ////Create temp file from received file extension
                //fs = new FileStream("temp." + fileDet.FILETYPE, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
                //fs.Write(receiveBytes, 0, receiveBytes.Length);

                //Console.WriteLine("---File Saved----" + receiveBytes.Length.ToString());
                //Console.WriteLine("---Opening file with associated program----");

                //Process.Start(fs.Name); //Opens file with associated program

                #region big
                while (true)
                {
                    subreceiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint);
                    Array.Copy(subreceiveBytes, subreceiveBytes.Length - 1, a, 0, 1);
                    string returnData = Encoding.ASCII.GetString(a);
                    if (returnData.IndexOf((char)3) > -1)
                    {
                        Array.Copy(subreceiveBytes, 0, receiveBytes, insertpoint, subreceiveBytes.Length - 1);

                        //Convert and display data
                        Console.WriteLine("------File received...Saving....");

                        //Create temp file from received file extension
                        fs = new FileStream("temp." + fileDet.FILETYPE, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
                        fs.Write(receiveBytes, 0, receiveBytes.Length);

                        Console.WriteLine("---File Saved----" + receiveBytes.Length.ToString());
                        Console.WriteLine("---Opening file with associated program----");

                        Process.Start(fs.Name); //Opens file with associated program
                    }
                    else
                    {
                        i++;
                        Console.WriteLine(i.ToString());
                        Array.Copy(subreceiveBytes, 0, receiveBytes, insertpoint, subreceiveBytes.Length);
                        insertpoint += subreceiveBytes.Length;
                    }
                }
                #endregion
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                fs.Close();
                receivingUdpClient.Close();
            }
        }
}
}
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值