iframe+servlet上传文件显示进度(不使用session) .

http://blog.csdn.net/tom_221x/article/details/3777064  网页

 

 

 

先看图:

      csdn不给传,气啊!不传了。

原理:

      使用了servlet,2个iframe,commons-fileupload的组件。2个iframe比较传统一个是上传文件数据,一个是更新页面进度条的。因为如果文件超大的话,在文件数据没有传完之前是不能回写数据的,所以一个iframe不行的。我这里没有使用session保存进度数据,我在网上看到不少的实现都是用session,最近在看rest觉得session不太符合rest风格。

      我是利用线程+同步hashmap是实现的。 具体是这样的,页面2个iframe首次的请求不是传文件请求,利用一个iframe的src属性请求到servlet没有什么参数.这个iframe也是以后更新页面进度条的iframe.在这次请求中,构造进度条监听器,产生一个唯一的字符串作为key,映射到这个监听器对象。然后呢,把利用iframe push js脚本调用客户端的另一个iframe上传文件,并且把key发回去作为参数传回来。这时候要阻塞当前线程利用循环就可以了。 第二个上传线程来的时候,获取key,从同步hashmap中得到监听器,把监听器给上传线程的commons-fileupload的对象。这样两个线程就能通过监听器对象通信进度数据了。最后在阻塞的那个iframe中不断的push脚本更新页面就可以了。

    测试了ie,firefox,chrome的都可以除了ie有点呆,多线程也测了ok的。下面看代码,很简单就一个页面一个servlet。

    

  1. <%@ page language="java"  pageEncoding="utf-8"%>  
  2. <%  
  3. String path = request.getContextPath();  
  4. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";  
  5. %>  
  6. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  7. <html>  
  8.   <head>  
  9.     <base href="<%=basePath%>">  
  10.       
  11.     <title>ajax显示上传文件进度</title>  
  12.       
  13.     <meta http-equiv="pragma" content="no-cache">  
  14.     <meta http-equiv="cache-control" content="no-cache">  
  15.     <meta http-equiv="expires" content="0">      
  16.     <!-- 
  17.     <link rel="stylesheet" type="text/css" href="styles.css" mce_href="styles.css"> 
  18.     -->  
  19.     <mce:style type="text/css"><!--  
  20.         iframe{  
  21.             border:0;  
  22.         }  
  23.         #p_out{  
  24.           width:200px;  
  25.           height:12px;  
  26.           margin:10px 0 0 0;  
  27.           padding:1px 1px 1px 1px;  
  28.           font-size:10px;  
  29.           border:solid #d4e4ff 1px;  
  30.         }  
  31.         #p_in{  
  32.           width:0%;  
  33.           height:100%;  
  34.           background-color:#d4e4ff;  
  35.           margin:0;  
  36.           padding:0;  
  37.         }  
  38.         #dis{  
  39.           margin:0;  
  40.           padding:0;  
  41.           text-align:center;  
  42.           font-size:12px;  
  43.           height:12px;  
  44.           width:200px;  
  45.         }  
  46.       
  47. --></mce:style><mce:style type="text/css" mce_bogus="1"><!--  
  48.         iframe{  
  49.             border:0;  
  50.         }  
  51.         #p_out{  
  52.           width:200px;  
  53.           height:12px;  
  54.           margin:10px 0 0 0;  
  55.           padding:1px 1px 1px 1px;  
  56.           font-size:10px;  
  57.           border:solid #d4e4ff 1px;  
  58.         }  
  59.         #p_in{  
  60.           width:0%;  
  61.           height:100%;  
  62.           background-color:#d4e4ff;  
  63.           margin:0;  
  64.           padding:0;  
  65.         }  
  66.         #dis{  
  67.           margin:0;  
  68.           padding:0;  
  69.           text-align:center;  
  70.           font-size:12px;  
  71.           height:12px;  
  72.           width:200px;  
  73.         }  
  74.       
  75. --></mce:style><style type="text/css" mce_bogus="1" mce_bogus="1">       iframe{  
  76.             border:0;  
  77.         }  
  78.         #p_out{  
  79.           width:200px;  
  80.           height:12px;  
  81.           margin:10px 0 0 0;  
  82.           padding:1px 1px 1px 1px;  
  83.           font-size:10px;  
  84.           border:solid #d4e4ff 1px;  
  85.         }  
  86.         #p_in{  
  87.           width:0%;  
  88.           height:100%;  
  89.           background-color:#d4e4ff;  
  90.           margin:0;  
  91.           padding:0;  
  92.         }  
  93.         #dis{  
  94.           margin:0;  
  95.           padding:0;  
  96.           text-align:center;  
  97.           font-size:12px;  
  98.           height:12px;  
  99.           width:200px;  
  100.         }  
  101.     </style>  
  102.   </head>  
  103.     
  104.   <body>  
  105.     <form id="uploadfile_form" name="uploadfile_form" enctype="multipart/form-data"  
  106.      method="post" target="uploadfile_iframe">  
  107.           
  108.         <input type="file" name="file" />  
  109.         <br><br>  
  110.         <button onclick="progress()">提交</button>  
  111.           
  112.         <div id="p_out"><div id="p_in"></div></div>  
  113.         <div id="dis"></div>  
  114.     </form>  
  115.     <iframe width="0px" height="0px" id="uploadfile_iframe" name="uploadfile_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe>  
  116.     <iframe width="0px" height="0px" id="progress_iframe" name="progress_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe>  
  117.   </body>  
  118.     
  119.   <mce:script type="text/javascript"><!--  
  120.     function progress(){  
  121.         document.getElementById('progress_iframe').src = 'UploadFile';  
  122.         document.getElementById('dis').innerHTML = '初始化数据...';  
  123.     }  
  124.       
  125.     function uploadFile(k){  
  126.         document.forms[0].action = 'UploadFile?key='+k;  
  127.         document.forms[0].submit();  
  128.     }  
  129.       
  130.     function upload(len,total){  
  131.         document.getElementById('p_in').style.width = (Math.round(len/total*100))+'%';  
  132.         document.getElementById('dis').innerHTML = len+'/'+total+' Byte';  
  133.     }  
  134.       
  135.     
  136. // --></mce:script>  
  137. </html>  
<%@ page language="java" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>ajax显示上传文件进度</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <!-- <link rel="stylesheet" type="text/css" href="styles.css" mce_href="styles.css"> --> <mce:style type="text/css"><!-- iframe{ border:0; } #p_out{ width:200px; height:12px; margin:10px 0 0 0; padding:1px 1px 1px 1px; font-size:10px; border:solid #d4e4ff 1px; } #p_in{ width:0%; height:100%; background-color:#d4e4ff; margin:0; padding:0; } #dis{ margin:0; padding:0; text-align:center; font-size:12px; height:12px; width:200px; } --></mce:style><mce:style type="text/css" mce_bogus="1"><!-- iframe{ border:0; } #p_out{ width:200px; height:12px; margin:10px 0 0 0; padding:1px 1px 1px 1px; font-size:10px; border:solid #d4e4ff 1px; } #p_in{ width:0%; height:100%; background-color:#d4e4ff; margin:0; padding:0; } #dis{ margin:0; padding:0; text-align:center; font-size:12px; height:12px; width:200px; } --></mce:style><style type="text/css" mce_bogus="1" mce_bogus="1"> iframe{ border:0; } #p_out{ width:200px; height:12px; margin:10px 0 0 0; padding:1px 1px 1px 1px; font-size:10px; border:solid #d4e4ff 1px; } #p_in{ width:0%; height:100%; background-color:#d4e4ff; margin:0; padding:0; } #dis{ margin:0; padding:0; text-align:center; font-size:12px; height:12px; width:200px; } </style> </head> <body> <form id="uploadfile_form" name="uploadfile_form" enctype="multipart/form-data" method="post" target="uploadfile_iframe"> <input type="file" name="file" /> <br><br> <button οnclick="progress()">提交</button> <div id="p_out"><div id="p_in"></div></div> <div id="dis"></div> </form> <iframe width="0px" height="0px" id="uploadfile_iframe" name="uploadfile_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe> <iframe width="0px" height="0px" id="progress_iframe" name="progress_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe> </body> <mce:script type="text/javascript"><!-- function progress(){ document.getElementById('progress_iframe').src = 'UploadFile'; document.getElementById('dis').innerHTML = '初始化数据...'; } function uploadFile(k){ document.forms[0].action = 'UploadFile?key='+k; document.forms[0].submit(); } function upload(len,total){ document.getElementById('p_in').style.width = (Math.round(len/total*100))+'%'; document.getElementById('dis').innerHTML = len+'/'+total+' Byte'; } // --></mce:script> </html> 

    

  1. package com.scott.uploadfile;  
  2. import java.io.File;  
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.io.PrintWriter;  
  7. import java.net.URLEncoder;  
  8. import java.util.Iterator;  
  9. import java.util.List;  
  10. import java.util.concurrent.ConcurrentHashMap;  
  11. import javax.servlet.ServletException;  
  12. import javax.servlet.http.HttpServlet;  
  13. import javax.servlet.http.HttpServletRequest;  
  14. import javax.servlet.http.HttpServletResponse;  
  15. import org.apache.commons.fileupload.FileItem;  
  16. import org.apache.commons.fileupload.FileUploadException;  
  17. import org.apache.commons.fileupload.ProgressListener;  
  18. import org.apache.commons.fileupload.disk.DiskFileItemFactory;  
  19. import org.apache.commons.fileupload.servlet.ServletFileUpload;  
  20. import com.scott.log.Log;  
  21. /** 
  22.  * @author scott.wanglei 
  23.  * @since  2009-1-8 
  24.  * 使用多线程实现文件进度的显示 
  25.  */  
  26. public class UploadFile extends HttpServlet {  
  27.     private static final long serialVersionUID = 4030816613803833495L;  
  28.     //同步散列表保存文件上传进度类的引用   
  29.     private ConcurrentHashMap<String,Progress> chp = new ConcurrentHashMap<String,Progress>();  
  30.     public UploadFile() {  
  31.         super();  
  32.     }  
  33.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  34.             throws ServletException, IOException {  
  35.              this.doPost(request, response);  
  36.     }  
  37.     @SuppressWarnings("deprecation")  
  38.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  39.             throws ServletException, IOException {  
  40.               
  41.              Log.info("客户端提交类型: "+request.getContentType());  
  42.               
  43.              response.setContentType("text/html;charset=UTF-8");  
  44.              response.setHeader("pragma", "no-cache");  
  45.              response.setHeader("cache-control", "no-cache");  
  46.              response.setHeader("expires", "0");  
  47.                
  48.              if(request.getContentType() == null){  
  49.                  Progress p = new Progress();   
  50.                  //根据上request对象产生的String的hashcode值做为key值   
  51.                  String key = Integer.toString(request.toString().hashCode());  
  52.                  Log.info("key1 = "+key);  
  53.                  //根据key值储存当前文件的进度对象引用   
  54.                  chp.put(key, p);  
  55.                  //把key传回并发起上传文件的请求   
  56.                  this.sendMsg(response,"parent.uploadFile('"+key+"')");  
  57.                  //上传线程没有开始时候阻塞更新线程   
  58.                  while(true){  
  59.                      if(p.getLength() != )  
  60.                          break;  
  61.                  }  
  62.                  long temp = ;  
  63.                  while(! p.isComplete){  
  64.                      if(temp != p.getCurrentLength()){  
  65.                          temp = p.getCurrentLength();  
  66.                          //更新客户端进度   
  67.                          this.sendMsg(response, "parent.upload('"+temp+"','"+p.getLength()+"')");  
  68.                            
  69.                      }  
  70.                  }  
  71.              }   
  72.              else if(request.getContentType().indexOf("multipart/form-data") > -1){  
  73.                    //上传线程开始执行   
  74.                    String key = request.getParameter("key");  
  75.                    Log.info("key2 = "+key);  
  76.                    Progress p = chp.get(key);  
  77.                    if(p != null){  
  78.                       p.setLength(request.getContentLength());  
  79.                       Log.info("文件长度: "+request.getContentLength());  
  80.                       this.startUploadFile(request,response, p,request.getRealPath("/"),key);  
  81.                       chp.remove(key);  
  82.                    }  
  83.                    else{  
  84.                        Log.info("Progress is null,k1 is not equal k2 ");  
  85.                        this.sendMsg(response, "alert('Progress is null,k1 is not equal k2')");  
  86.                          
  87.                    }  
  88.                     
  89.              }   
  90.              else {  
  91.                  Log.redInfo("ContentType is not correctly");  
  92.                  this.sendMsg(response, "alert('ContentType is not correctly')");     
  93.                    
  94.              }  
  95.                
  96.                
  97.     }  
  98.       
  99.     //上传文件   
  100.     @SuppressWarnings({ "deprecation", "unchecked"})  
  101.     private void startUploadFile( HttpServletRequest request, HttpServletResponse response,Progress p,   
  102.             String path,String key)  
  103.             throws IOException{  
  104.             
  105.                //设置上传工厂   
  106.                DiskFileItemFactory factory = new DiskFileItemFactory();   
  107.                Log.info("上传临时路径: "+path);  
  108.                factory.setRepository(new File(path));  
  109.                //阀值,超过这个值才会写到临时目录   
  110.                factory.setSizeThreshold(1024*1024*10);   
  111.                ServletFileUpload upload = new ServletFileUpload(factory);  
  112.                //最大上传限制   
  113.                upload.setSizeMax(1024*1024*200);  
  114.                //设置监听器监听上传进度   
  115.                upload.setProgressListener(p);  
  116.                List<FileItem> items = null;  
  117.                try {  
  118.                    items = upload.parseRequest(request);  
  119.                } catch (FileUploadException e) {  
  120.                 e.printStackTrace();  
  121.                 this.errorAndStorp(response, "alert('FileUploadExeception happened')", p,key);  
  122.                }  
  123.                  
  124.                for(Iterator<FileItem> it = items.iterator();it.hasNext();){  
  125.                    FileItem item = it.next();  
  126.                    if(item.isFormField()){  
  127.                        //处理表单域   
  128.                    }  
  129.                    else{  
  130.                        try {  
  131.                            Log.info("上传路径: "+path+item.getName());  
  132.                            FileOutputStream  fos = new FileOutputStream(path+URLEncoder.encode(item.getName(),"utf-8"));  
  133.                            if(item.isInMemory()){//文件全在内存中   
  134.                                fos.write(item.get());  
  135.                                p.setComplete(true);  
  136.                            }  
  137.                            else{  
  138.                                InputStream is = item.getInputStream();  
  139.                                byte[] buffer = new byte[1024];  
  140.                                int len;  
  141.                                while((len = is.read(buffer)) > ){  
  142.                                    fos.write(buffer, , len);  
  143.                                }  
  144.                                p.setComplete(true);  
  145.                                is.close();  
  146.                                fos.close();     
  147.                            }  
  148.                        } catch (Exception e) {  
  149.                            e.printStackTrace();  
  150.                            this.errorAndStorp(response, "alert('Exception happened')", p,key);}                    
  151.                    }  
  152.                }  
  153.     }  
  154.       
  155.     //上传线程一旦出现意外,就让progress出于完成状态以终止向客户端发送信息的线程   
  156.     private void errorAndStorp(HttpServletResponse response,String script,  
  157.             Progress p,String key)   
  158.            throws IOException{  
  159.           
  160.         p.setComplete(true);  
  161.         chp.remove(key);  
  162.         this.sendMsg(response, script);  
  163.     }  
  164.       
  165.     //向客户端发送数据   
  166.     private void sendMsg(HttpServletResponse response,String script) throws IOException{  
  167.       PrintWriter out = response.getWriter();  
  168.       out.println("<mce:script type='text/javascript'><!--  
  169. "+script+"  
  170. // --></mce:script>");   
  171.       //ie太拽了发送的字节少了它根本不掉你   
  172.       out.println("---------------------------------------------------");  
  173.       out.println("---------------------------------------------------");  
  174.       out.println("---------------------------------------------------");  
  175.       out.println("---------------------------------------------------");  
  176.       out.flush();  
  177.     }  
  178.       
  179.     //存放文件上传进度   
  180.     private static class Progress implements ProgressListener{  
  181.           
  182.         private  long length = ;//文件总长度   
  183.         private long currentLength = ;//已上传的文件长度   
  184.         private boolean isComplete = false;//上传是否完成   
  185.           
  186.         //实现监听器方法实时更新进度属性   
  187.         public void update(long bytesRead, long contentLength, int items) {  
  188.             currentLength = bytesRead;  
  189.         }  
  190.           
  191.         public Progress(){}  
  192.           
  193.         public long getLength() {  
  194.             return length;  
  195.         }  
  196.         public void setLength(int l){  
  197.             this.length = l;  
  198.         }  
  199.         public long getCurrentLength() {  
  200.             return currentLength;  
  201.         }  
  202.           
  203.         public void setCurrentLenght(int cl){  
  204.             this.currentLength = cl;  
  205.         }  
  206.         public boolean isComplete() {  
  207.             return isComplete;  
  208.         }  
  209.         public void setComplete(boolean isComplete) {  
  210.             this.isComplete = isComplete;  
  211.         }  
  212.     }  
  213. }  
package com.scott.uploadfile; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.net.URLEncoder; import java.util.Iterator; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.ProgressListener; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import com.scott.log.Log; /** * @author scott.wanglei * @since 2009-1-8 * 使用多线程实现文件进度的显示 */ public class UploadFile extends HttpServlet { private static final long serialVersionUID = 4030816613803833495L; //同步散列表保存文件上传进度类的引用 private ConcurrentHashMap<String,Progress> chp = new ConcurrentHashMap<String,Progress>(); public UploadFile() { super(); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @SuppressWarnings("deprecation") public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Log.info("客户端提交类型: "+request.getContentType()); response.setContentType("text/html;charset=UTF-8"); response.setHeader("pragma", "no-cache"); response.setHeader("cache-control", "no-cache"); response.setHeader("expires", "0"); if(request.getContentType() == null){ Progress p = new Progress(); //根据上request对象产生的String的hashcode值做为key值 String key = Integer.toString(request.toString().hashCode()); Log.info("key1 = "+key); //根据key值储存当前文件的进度对象引用 chp.put(key, p); //把key传回并发起上传文件的请求 this.sendMsg(response,"parent.uploadFile('"+key+"')"); //上传线程没有开始时候阻塞更新线程 while(true){ if(p.getLength() != 0) break; } long temp = 0; while(! p.isComplete){ if(temp != p.getCurrentLength()){ temp = p.getCurrentLength(); //更新客户端进度 this.sendMsg(response, "parent.upload('"+temp+"','"+p.getLength()+"')"); } } } else if(request.getContentType().indexOf("multipart/form-data") > -1){ //上传线程开始执行 String key = request.getParameter("key"); Log.info("key2 = "+key); Progress p = chp.get(key); if(p != null){ p.setLength(request.getContentLength()); Log.info("文件长度: "+request.getContentLength()); this.startUploadFile(request,response, p,request.getRealPath("/"),key); chp.remove(key); } else{ Log.info("Progress is null,k1 is not equal k2 "); this.sendMsg(response, "alert('Progress is null,k1 is not equal k2')"); } } else { Log.redInfo("ContentType is not correctly"); this.sendMsg(response, "alert('ContentType is not correctly')"); } } //上传文件 @SuppressWarnings({ "deprecation", "unchecked"}) private void startUploadFile( HttpServletRequest request, HttpServletResponse response,Progress p, String path,String key) throws IOException{ //设置上传工厂 DiskFileItemFactory factory = new DiskFileItemFactory(); Log.info("上传临时路径: "+path); factory.setRepository(new File(path)); //阀值,超过这个值才会写到临时目录 factory.setSizeThreshold(1024*1024*10); ServletFileUpload upload = new ServletFileUpload(factory); //最大上传限制 upload.setSizeMax(1024*1024*200); //设置监听器监听上传进度 upload.setProgressListener(p); List<FileItem> items = null; try { items = upload.parseRequest(request); } catch (FileUploadException e) { e.printStackTrace(); this.errorAndStorp(response, "alert('FileUploadExeception happened')", p,key); } for(Iterator<FileItem> it = items.iterator();it.hasNext();){ FileItem item = it.next(); if(item.isFormField()){ //处理表单域 } else{ try { Log.info("上传路径: "+path+item.getName()); FileOutputStream fos = new FileOutputStream(path+URLEncoder.encode(item.getName(),"utf-8")); if(item.isInMemory()){//文件全在内存中 fos.write(item.get()); p.setComplete(true); } else{ InputStream is = item.getInputStream(); byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) > 0){ fos.write(buffer, 0, len); } p.setComplete(true); is.close(); fos.close(); } } catch (Exception e) { e.printStackTrace(); this.errorAndStorp(response, "alert('Exception happened')", p,key);} } } } //上传线程一旦出现意外,就让progress出于完成状态以终止向客户端发送信息的线程 private void errorAndStorp(HttpServletResponse response,String script, Progress p,String key) throws IOException{ p.setComplete(true); chp.remove(key); this.sendMsg(response, script); } //向客户端发送数据 private void sendMsg(HttpServletResponse response,String script) throws IOException{ PrintWriter out = response.getWriter(); out.println("<mce:script type='text/javascript'><!-- "+script+" // --></mce:script>"); //ie太拽了发送的字节少了它根本不掉你 out.println("---------------------------------------------------"); out.println("---------------------------------------------------"); out.println("---------------------------------------------------"); out.println("---------------------------------------------------"); out.flush(); } //存放文件上传进度 private static class Progress implements ProgressListener{ private long length = 0;//文件总长度 private long currentLength = 0;//已上传的文件长度 private boolean isComplete = false;//上传是否完成 //实现监听器方法实时更新进度属性 public void update(long bytesRead, long contentLength, int items) { currentLength = bytesRead; } public Progress(){} public long getLength() { return length; } public void setLength(int l){ this.length = l; } public long getCurrentLength() { return currentLength; } public void setCurrentLenght(int cl){ this.currentLength = cl; } public boolean isComplete() { return isComplete; } public void setComplete(boolean isComplete) { this.isComplete = isComplete; } } } 

总结:

      1. 在ie中document.frames['name'].src 和windows.frames['name'].src的赋值都是没有权限的。只有用

          document.getElementById(' ').src可以的。

      2.  在servlet中response.getWriter().flush();是不能让IE做出反应的,除非response.getWriter().close().或者回

           写的字节流要够多才行的。

      3. form的enctype="multipart/form-data"的时候,如果在url后用?带参数的话,在后request,getParametes("")

          还是能取到?后面带的参数的,并且from表单里没有这个参数。

      4. 在servlet里缓存request和response充满了不确定性的,因为servlet的结束了request和resposne也发生了变

          化。

转载于:https://my.oschina.net/meSpace/blog/41914

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值