WebClient, 以字符串的形式上传/下载数据, 以流的方式上传/下载数据(转)

作者: webabcd


介绍
Silverlight 2.0 详解WebClient,以字符串的形式上传、下载数据;以流的方式上传、下载数据
    WebClient - 将数据发送到指定的 URI,或者从指定的 URI 接收数据的类
        DownloadStringAsync(Uri address, Object userToken) - 以字符串的形式下载指定的 URI 的资源
        UploadStringAsync(Uri address, string data) - 以字符串的形式上传数据到指定的 URI。所使用的 HTTP 方法默认为 POST
        OpenReadAsync(Uri address, Object userToken) - 以流的形式下载指定的 URI 的资源
        OpenWriteAsync(Uri address, string method, Object userToken) - 打开流以使用指定的方法向指定的 URI 写入数据

示例
1、以字符串的形式和流的形式下载数据
WebClientDownload.xaml
< UserControl  x:Class ="Silverlight20.Communication.WebClientDownload"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml" >
     < StackPanel  HorizontalAlignment ="Left"  Orientation ="Horizontal" >
    
         < StackPanel  Margin ="5"  Width ="200" >
             < TextBox  x:Name ="lblMsgString"  Margin ="5"   />
             < ProgressBar  x:Name ="progressBarString"  Height ="20"  Margin ="5"  Minimum ="0"  Maximum ="100"   />
         </ StackPanel >
        
         < StackPanel  Margin ="5"  Width ="200" >
             < TextBox  x:Name ="lblMsgStream"  Margin ="5"   />
             < ProgressBar  x:Name ="progressBarStream"  Height ="20"  Margin ="5"  Minimum ="0"  Maximum ="100"   />
             < Image  x:Name ="img"  Margin ="5"   />
         </ StackPanel >
        
     </ StackPanel >
</ UserControl >

WebClientDownload.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.IO;

namespace Silverlight20.Communication
{
     public  partial  class WebClientDownload : UserControl
    {
         //  用于演示以字符串的形式下载数据
         string _urlString =  " http://localhost/Files/Demo.zip ";

         //  用于演示以流的形式下载数据
         string _urlStream =  " http://localhost/Files/Logo.png ";

         public WebClientDownload()
        {
            InitializeComponent();

             //  演示字符串式下载
            DownloadStringDemo();

             //  演示流式下载
            DownloadStreamDemo();
        }

         /**/ ///   <summary>
        
///  演示字符串式下载
        
///   </summary>
         void DownloadStringDemo()
        {
            Uri uri =  new Uri(_urlString, UriKind.Absolute);

             /**//*  
             * WebClient - 将数据发送到指定的 URI,或者从指定的 URI 接收数据的类
             *     DownloadStringCompleted - 下载数据完毕后(包括取消操作及有错误发生时)所触发的事件
             *     DownloadProgressChanged - 下载数据过程中所触发的事件。正在下载或下载完全部数据后会触发
             *     DownloadStringAsync(Uri address, Object userToken) - 以字符串的形式下载指定的 URI 的资源
             *         Uri address - 需要下载的资源地址
             *         Object userToken - 用户标识
             
*/

            System.Net.WebClient clientDownloadString =  new System.Net.WebClient();

            clientDownloadString.DownloadStringCompleted +=  new DownloadStringCompletedEventHandler(clientDownloadString_DownloadStringCompleted);
            clientDownloadString.DownloadProgressChanged +=  new DownloadProgressChangedEventHandler(clientDownloadString_DownloadProgressChanged);
            clientDownloadString.DownloadStringAsync(uri,  " userToken ");
        }

         void clientDownloadString_DownloadProgressChanged( object sender, DownloadProgressChangedEventArgs e)
        {
             /**//*
             * DownloadProgressChangedEventArgs.ProgressPercentage - 下载完成的百分比
             * DownloadProgressChangedEventArgs.BytesReceived - 当前收到的字节数
             * DownloadProgressChangedEventArgs.TotalBytesToReceive - 总共需要下载的字节数
             * DownloadProgressChangedEventArgs.UserState - 用户标识
             
*/

            lblMsgString.Text =  string.Format( " 下载完成的百分比:{0}\r\n当前收到的字节数:{1}\r\n总共需要下载的字节数:{2}\r\n ",
                e.ProgressPercentage.ToString() +  " % ",
                e.BytesReceived.ToString(),
                e.TotalBytesToReceive.ToString());

            progressBarString.Value = ( double)e.ProgressPercentage;
        }

         void clientDownloadString_DownloadStringCompleted( object sender, DownloadStringCompletedEventArgs e)
        {
             /**//*
             * DownloadStringCompletedEventArgs.Error - 该异步操作期间是否发生了错误
             * DownloadStringCompletedEventArgs.Cancelled - 该异步操作是否已被取消
             * DownloadStringCompletedEventArgs.Result - 下载后的字符串类型的数据
             * DownloadStringCompletedEventArgs.UserState - 用户标识
             
*/

             if (e.Error !=  null)
            {
                lblMsgString.Text += e.Error.ToString();
                 return;
            }

             if (e.Cancelled !=  true)
            {
                lblMsgString.Text +=  string.Format( " 用户标识:{0} ", e.UserState.ToString());
            }
        }



         /**/ ///   <summary>
        
///  演示流式下载
        
///   </summary>
         void DownloadStreamDemo()
        {
            Uri uri =  new Uri(_urlStream, UriKind.Absolute);

             /**//*  
             * WebClient - 将数据发送到指定的 URI,或者从指定的 URI 接收数据的类
             *     IsBusy - 指定的web请求是否正在进行中
             *     CancelAsync() - 取消指定的异步操作 
             *     OpenReadCompleted - 数据读取完毕后(包括取消操作及有错误发生时)所触发的事件。流的方式
             *     DownloadProgressChanged - 下载数据过程中所触发的事件。正在下载或下载完全部数据后会触发
             *     OpenReadAsync(Uri address, Object userToken) - 以流的形式下载指定的 URI 的资源
             *         Uri address - 需要下载的资源地址
             *         Object userToken - 用户标识
             
*/

            System.Net.WebClient clientDownloadStream =  new System.Net.WebClient();

             if (clientDownloadStream.IsBusy)
                clientDownloadStream.CancelAsync();

            clientDownloadStream.OpenReadCompleted +=  new OpenReadCompletedEventHandler(clientDownloadStream_OpenReadCompleted);
            clientDownloadStream.DownloadProgressChanged +=  new DownloadProgressChangedEventHandler(clientDownloadStream_DownloadProgressChanged);
            clientDownloadStream.OpenReadAsync(uri);
        }

         void clientDownloadStream_DownloadProgressChanged( object sender, DownloadProgressChangedEventArgs e)
        {
             /**//*
             * DownloadProgressChangedEventArgs.ProgressPercentage - 下载完成的百分比
             * DownloadProgressChangedEventArgs.BytesReceived - 当前收到的字节数
             * DownloadProgressChangedEventArgs.TotalBytesToReceive - 总共需要下载的字节数
             * DownloadProgressChangedEventArgs.UserState - 用户标识
             
*/

            lblMsgString.Text =  string.Format( " 下载完成的百分比:{0}\r\n当前收到的字节数:{1}\r\n总共需要下载的字节数:{2}\r\n ",
                e.ProgressPercentage.ToString() +  " % ",
                e.BytesReceived.ToString(),
                e.TotalBytesToReceive.ToString());

            progressBarStream.Value = ( double)e.ProgressPercentage;
        }

         void clientDownloadStream_OpenReadCompleted( object sender, OpenReadCompletedEventArgs e)
        {
             /**//*
             * OpenReadCompletedEventArgs.Error - 该异步操作期间是否发生了错误
             * OpenReadCompletedEventArgs.Cancelled - 该异步操作是否已被取消
             * OpenReadCompletedEventArgs.Result - 下载后的 Stream 类型的数据
             * OpenReadCompletedEventArgs.UserState - 用户标识
             
*/

             if (e.Error !=  null)
            {
                lblMsgStream.Text += e.Error.ToString();
                 return;
            }

             if (e.Cancelled !=  true)
            {
                System.Windows.Media.Imaging.BitmapImage imageSource =  new System.Windows.Media.Imaging.BitmapImage();
                imageSource.SetSource(e.Result);
                img.Source = imageSource;
            }
        }
    }
}

2、以字符串的形式和流的形式上传数据
REST.cs(WCF创建的用于演示以字符串的形式和流的形式上传数据的REST服务)

using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;

using System.ServiceModel.Web;
using System.Collections.Generic;
using System.Text;
using System.IO;

/**/ ///   <summary>
///  提供 REST 服务的类
///  注:Silverlight只支持 GET 和 POST
///   </summary>
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public  class REST
{
     /**/ ///   <summary>
    
///  用于演示返回 JSON(对象) 的 REST 服务
    
///   </summary>
    
///   <param name="name"></param>
    
///   <returns></returns>
    [OperationContract]
    [WebGet(UriTemplate =  " User/{name}/json ", ResponseFormat = WebMessageFormat.Json)]
     public User HelloJson( string name)
    {
         return  new User { Name = name, DayOfBirth =  new DateTime( 1980214) };
    }

     /**/ ///   <summary>
    
///  用于演示返回 JSON(集合) 的 REST 服务
    
///   </summary>
    
///   <returns></returns>
    [OperationContract]
    [WebGet(UriTemplate =  " Users/json ", ResponseFormat = WebMessageFormat.Json)]
     public List<User> HelloJson2()
    {
         return  new List<User> 
        { 
             new User(){ Name =  " webabcd01 ", DayOfBirth =  new DateTime( 198011) },
             new User(){ Name =  " webabcd02 ", DayOfBirth =  new DateTime( 198022) },
             new User(){ Name =  " webabcd03 ", DayOfBirth =  new DateTime( 198033) },
        };
    }

     /**/ ///   <summary>
    
///  用于演示返回 XML(对象) 的 REST 服务
    
///   </summary>
    
///   <param name="name"></param>
    
///   <returns></returns>
    [OperationContract]
    [WebGet(UriTemplate =  " User/{name}/xml ", ResponseFormat = WebMessageFormat.Xml)]
     public User HelloXml( string name)
    {
         return  new User { Name = name, DayOfBirth =  new DateTime( 1980214) };
    }

     /**/ ///   <summary>
    
///  用于演示返回 XML(集合) 的 REST 服务
    
///   </summary>
    
///   <returns></returns>
    [OperationContract]
    [WebGet(UriTemplate =  " Users/xml ", ResponseFormat = WebMessageFormat.Xml)]
     public List<User> HelloXml2()
    {
         return  new List<User> 
        { 
             new User(){ Name =  " webabcd01 ", DayOfBirth =  new DateTime( 198011) },
             new User(){ Name =  " webabcd02 ", DayOfBirth =  new DateTime( 198022) },
             new User(){ Name =  " webabcd03 ", DayOfBirth =  new DateTime( 198033) },
        };
    }

     /**/ ///   <summary>
    
///  用于演示以字符串的形式上传数据的 REST 服务
    
///   </summary>
    
///   <param name="fileName"> 上传的文件名 </param>
    
///   <param name="stream"> POST 过来的数据 </param>
    
///   <returns></returns>
    [OperationContract]
    [WebInvoke(UriTemplate =  " UploadString/?fileName={fileName} ", Method =  " POST ", ResponseFormat = WebMessageFormat.Json)]
     public  bool UploadString( string fileName, Stream stream)
    {
         //  文件的服务端保存路径
         string path = Path.Combine( " C:\\ ", fileName);

         try
        {
             using (StreamReader sr =  new StreamReader(stream))
            {
                 //  将 POST 过来的被 Base64 编码过字符串传换成 byte[]
                 byte[] buffer = Convert.FromBase64String(sr.ReadToEnd());

                 using (FileStream fs =  new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                     //  将文件写入到服务端
                    fs.Write(buffer,  0, buffer.Length);
                }
            }

             return  true;
        }
         catch
        {
             return  false;
        }
    }

     /**/ ///   <summary>
    
///  用于演示以流的形式上传数据的 REST 服务
    
///   </summary>
    
///   <param name="fileName"> 上传的文件名 </param>
    
///   <param name="stream"> POST 过来的数据(流的方式) </param>
    
///   <returns></returns>
    [OperationContract]
    [WebInvoke(UriTemplate =  " UploadStream/?fileName={fileName} ", Method =  " POST ", ResponseFormat = WebMessageFormat.Json)]
     public  bool UploadStream( string fileName, Stream stream)
    {
         //  文件的服务端保存路径
         string path = Path.Combine( " C:\\ ", fileName);

         try
        {
             using (FileStream fs =  new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                 byte[] buffer =  new  byte[ 4096];
                 int count =  0;

                 //  每 POST 过来 4096 字节的数据,往服务端写一次
                 while ((count = stream.Read(buffer,  0, buffer.Length)) >  0)
                {
                    fs.Write(buffer,  0, count);
                }
            }

             return  true;
        }
         catch
        {
             return  false;
        }
    }
}

WebClientUpload.xaml

< UserControl  x:Class ="Silverlight20.Communication.WebClientUpload"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml" >
     < StackPanel  HorizontalAlignment ="Left"  Orientation ="Horizontal" >      
      
         < StackPanel  Margin ="5"  Width ="200" >
             < TextBox  x:Name ="lblMsgString"  Margin ="5"   />
             < ProgressBar  x:Name ="progressBarString"  Height ="20"  Margin ="5"  Minimum ="0"  Maximum ="100"   />
             < Button  x:Name ="btnString"  Content ="上传文件(字符串的方式)"  Margin ="5"  Click ="btnString_Click"   />
         </ StackPanel >
        
         < StackPanel  Margin ="5"  Width ="200" >
             < TextBox  x:Name ="lblMsgStream"  Margin ="5"   />
             < ProgressBar  x:Name ="progressBarStream"  Height ="20"  Margin ="5"  Minimum ="0"  Maximum ="100"   />
             < Button  x:Name ="btnStream"  Content ="上传文件(流的方式)"  Margin ="5"  Click ="btnStream_Click"   />
         </ StackPanel >
        
     </ StackPanel >
</ UserControl >

WebClientUpload.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.IO;
using System.Windows.Resources;
using System.ComponentModel;
using System.Windows.Browser;

namespace Silverlight20.Communication
{
     public  partial  class WebClientUpload : UserControl
    {
         //  用于演示以字符串的形式上传数据
         string _urlString =  " http://localhost:3036/REST.svc/UploadString/?fileName= ";

         //  用于演示以流的形式上传数据
         string _urlStream =  " http://localhost:3036/REST.svc/UploadStream/?fileName= ";

         public WebClientUpload()
        {
            InitializeComponent();
        }

         /**/ ///   <summary>
        
///  演示字符串式上传
        
///   </summary>
        
///   <param name="sender"></param>
        
///   <param name="e"></param>
         private  void btnString_Click( object sender, RoutedEventArgs e)
        {
             string data =  "";

             /**//*
             * OpenFileDialog - 文件对话框
             *     ShowDialog() - 显示文件对话框。在文件对话框中单击“确定”则返回true,反之则返回false
             *     File - 所选文件的 FileInfo 对象
             
*/

            OpenFileDialog dialog =  new OpenFileDialog();

             if (dialog.ShowDialog() ==  true)
            {
                 using (FileStream fs = dialog.File.OpenRead())
                {
                     byte[] buffer =  new  byte[fs.Length];
                    fs.Read(buffer,  0, buffer.Length);

                     //  将指定的 byte[] 转换为字符串(使用Base64编码)
                    data = Convert.ToBase64String(buffer);
                }

                 /**//*
                 * WebClient - 将数据发送到指定的 URI,或者从指定的 URI 接收数据的类
                 *     UploadStringCompleted - 上传数据完毕后(包括取消操作及有错误发生时)所触发的事件
                 *     UploadProgressChanged - 上传数据过程中所触发的事件。正在上传或上传完全部数据后会触发
                 *     Headers - 与请求相关的的标头的 key/value 对集合
                 *     UploadStringAsync(Uri address, string data) - 以字符串的形式上传数据到指定的 URI。所使用的 HTTP 方法默认为 POST
                 *         Uri address - 接收上传数据的 URI
                 *         string data - 需要上传的数据
                 
*/

                System.Net.WebClient clientUploadString =  new System.Net.WebClient();

                clientUploadString.UploadStringCompleted +=  new UploadStringCompletedEventHandler(clientUploadString_UploadStringCompleted);
                clientUploadString.UploadProgressChanged +=  new UploadProgressChangedEventHandler(clientUploadString_UploadProgressChanged);

                Uri uri =  new Uri(_urlString + dialog.File.Name, UriKind.Absolute);
                clientUploadString.Headers[ " Content-Type "] =  " application/x-www-form-urlencoded ";

                clientUploadString.UploadStringAsync(uri, data);
            }
        }

         void clientUploadString_UploadProgressChanged( object sender, UploadProgressChangedEventArgs e)
        {
             /**//*
             * UploadProgressChangedEventArgs.ProgressPercentage - 上传完成的百分比
             * UploadProgressChangedEventArgs.BytesSent - 当前发送的字节数
             * UploadProgressChangedEventArgs.TotalBytesToSend - 总共需要发送的字节数
             * UploadProgressChangedEventArgs.BytesReceived - 当前接收的字节数
             * UploadProgressChangedEventArgs.TotalBytesToReceive - 总共需要接收的字节数
             * UploadProgressChangedEventArgs.UserState - 用户标识
             
*/

            lblMsgString.Text =  string.Format( " 上传完成的百分比:{0}\r\n当前发送的字节数:{1}\r\n总共需要发送的字节数:{2}\r\n当前接收的字节数:{3}\r\n总共需要接收的字节数:{4}\r\n ",
                e.ProgressPercentage.ToString(),
                e.BytesSent.ToString(),
                e.TotalBytesToSend.ToString(),
                e.BytesReceived.ToString(),
                e.TotalBytesToReceive.ToString());

            progressBarString.Value = ( double)e.ProgressPercentage;
        }

         void clientUploadString_UploadStringCompleted( object sender, UploadStringCompletedEventArgs e)
        {
             /**//*
             * UploadStringCompletedEventArgs.Error - 该异步操作期间是否发生了错误
             * UploadStringCompletedEventArgs.Cancelled - 该异步操作是否已被取消
             * UploadStringCompletedEventArgs.Result - 服务端返回的数据(字符串类型)
             * UploadStringCompletedEventArgs.UserState - 用户标识
             
*/

             if (e.Error !=  null)
            {
                lblMsgString.Text += e.Error.ToString();
                 return;
            }

             if (e.Cancelled !=  true)
            {
                var jsonObject = System.Json.JsonObject.Parse(e.Result);
                
                lblMsgString.Text +=  string.Format( " 是否上传成功:{0} ",
                    ( bool)jsonObject);
            }
        }



         /**/ ///   <summary>
        
///  演示流式上传
        
///   </summary>
        
///   <param name="sender"></param>
        
///   <param name="e"></param>
         private  void btnStream_Click( object sender, RoutedEventArgs e)
        {
            FileStream fs =  null;

            OpenFileDialog dialog =  new OpenFileDialog();

             if (dialog.ShowDialog() ==  true)
            {
                fs = dialog.File.OpenRead();

                 /**//*
                 * WebClient - 将数据发送到指定的 URI,或者从指定的 URI 接收数据的类
                 *     OpenWriteCompleted - 在打开用于上传的流完成时(包括取消操作及有错误发生时)所触发的事件
                 *     WriteStreamClosed - 在写入数据流的异步操作完成时(包括取消操作及有错误发生时)所触发的事件
                 *     UploadProgressChanged - 上传数据过程中所触发的事件。如果调用 OpenWriteAsync() 则不会触发此事件
                 *     Headers - 与请求相关的的标头的 key/value 对集合
                 *     OpenWriteAsync(Uri address, string method, Object userToken) - 打开流以使用指定的方法向指定的 URI 写入数据
                 *         Uri address - 接收上传数据的 URI
                 *         string method - 所使用的 HTTP 方法(POST 或 GET)
                 *         Object userToken - 需要上传的数据流
                 
*/

                System.Net.WebClient clientUploadStream =  new System.Net.WebClient();

                clientUploadStream.OpenWriteCompleted +=  new OpenWriteCompletedEventHandler(clientUploadStream_OpenWriteCompleted);
                clientUploadStream.UploadProgressChanged +=  new UploadProgressChangedEventHandler(clientUploadStream_UploadProgressChanged);
                clientUploadStream.WriteStreamClosed +=  new WriteStreamClosedEventHandler(clientUploadStream_WriteStreamClosed);

                Uri uri =  new Uri(_urlStream + dialog.File.Name, UriKind.Absolute);
                clientUploadStream.Headers[ " Content-Type "] =  " multipart/form-data ";

                clientUploadStream.OpenWriteAsync(uri,  " POST ", fs);
            }
        }

         void clientUploadStream_UploadProgressChanged( object sender, UploadProgressChangedEventArgs e)
        {
             //  因为是调用 OpenWriteAsync(),所以不会触发 UploadProgressChanged 事件

            lblMsgString.Text =  string.Format( " 上传完成的百分比:{0}\r\n当前发送的字节数:{1}\r\n总共需要发送的字节数:{2}\r\n当前接收的字节数:{3}\r\n总共需要接收的字节数:{4}\r\n ",
                e.ProgressPercentage.ToString(),
                e.BytesSent.ToString(),
                e.TotalBytesToSend.ToString(),
                e.BytesReceived.ToString(),
                e.TotalBytesToReceive.ToString());

            progressBarStream.Value = ( double)e.ProgressPercentage;
        }

         void clientUploadStream_OpenWriteCompleted( object sender, OpenWriteCompletedEventArgs e)
        {
            System.Net.WebClient client = sender  as System.Net.WebClient;

             if (e.Error !=  null)
            {
                lblMsgStream.Text += e.Error.ToString();
                 return;
            }

             if (e.Cancelled !=  true)
            {
                 //  e.UserState - 需要上传的流(客户端流)
                Stream clientStream = e.UserState  as Stream;

                 //  e.Result - 目标地址的流(服务端流)
                Stream serverStream = e.Result;

                 byte[] buffer =  new  byte[ 4096];
                 int count =  0;

                 //  clientStream.Read - 将需要上传的流读取到指定的字节数组中
                 while ((count = clientStream.Read(buffer,  0, buffer.Length)) >  0)
                {
                     //  serverStream.Write - 将指定的字节数组写入到目标地址的流
                    serverStream.Write(buffer,  0, count);
                }

                serverStream.Close();
                clientStream.Close();
            }
        }

         void clientUploadStream_WriteStreamClosed( object sender, WriteStreamClosedEventArgs e)
        {
             if (e.Error !=  null)
            {
                lblMsgStream.Text += e.Error.ToString();
                 return;
            }
             else
            {
                lblMsgStream.Text +=  " 上传完成 ";
            }
        }
    }
}

/**//*
 * 其他:
 * 1、WebClient 对象一次只能启动一个请求。如果在一个请求完成(包括出错和取消)前,即IsBusy为true时,进行第二个请求,则第二个请求将会抛出 NotSupportedException 类型的异常
 * 2、如果 WebClient 对象的 BaseAddress 属性不为空,则 BaseAddress 与 URI(相对地址) 组合在一起构成绝对 URI
 * 3、WebClient 类的 AllowReadStreamBuffering 属性:是否对从 Internet 资源接收的数据做缓冲处理。默认值为true,将数据缓存在客户端内存中,以便随时被应用程序读取
 
*/





 


 

转载于:https://www.cnblogs.com/arongbest/archive/2011/10/09/2203706.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值