Struts的FormFile与Commons-FileUpload控件使用心得

Struts的FormFile与Commons-FileUp… 来自:http://www.iteye.com/topic/212566   前一段时间刚来公司,看到一个项目中以前有人写的struts代码。是使用了FormFile来处理关于文件上传的模块。但是用力一段时间后,发现出问题 了。写完的这个模块,上传文件是没有问题的,但是当服务器的空间较小的时候,穿一个比较大的文件就出问题了,文件还没有上传完,就抛出一个错误的页面,报 告上传模块出了问题,而且是To 阅读详情
前一段时间刚来公司,看到一个项目中以前有人写的struts代码。是使用了FormFile来处理关于文件上传的模块。但是用力一段时间后,发现出问题了。写完的这个模块,上传文件是没有问题的,但是当服务器的空间较小的时候,穿一个比较大的文件就出问题了,文件还没有上传完,就抛出一个错误的页面,报告上传模块出了问题,而且是Tomcat默认的出错页面。 

    于是想办法,修改,查看源代码,发现原来写这段代码的人是默认等文件上传完以后进入Action了才判断文件大小是否超出了限制。 

    但是,默认配置下使用struts的FormFile比较特殊,FormFile是struts包对外的一个接口,而且org.apache.struts.upload包是使用的commons-fileupload-1.0进行的封装。如果使用了它来实现文件上传的功能,则 必须是FormFile对象在被初始化以后才能使用 ,那什么时候它才是被初始化的呢? 

    答案是: 在进入Action之前就已经初始化好了!  

因此,原先的设计:在Action中判断文件大小是根本不能在上传过程中起到提示作用的,因为这时候文件已经上传完了。而且这个设计还有一个确定就是不能捕获上传过程中出现的任何问题。也就是说:在Action里我们得到的FormFile对象是上传的一个结果,而不是一个未上传好就可以使用的对象! 

    那如何控制FormFile上传的过程呢?显然,在Action里处理已经不能奏效了,想想别的办法,让我们翻看一下Struts的源代码找找灵感吧。 

    这是struts1.1的org.apache.struts.upload包的描述: 
    (见附件) 
    从上图我们可以看出有有CommonsMultipartRequestHandler和DiskMultipartRequestHandler两个类实现了MultipartRequestHandler接口。 

    大家都知道,Commons-fileupload控件在上传的时候,使用的 enctype为:enctype="multipart/form-data" ,因此不难看出MultipartRequestHandler的实现就是来处理enctype="multipart/form-data"这样的post请求的。 

    但是这里有两个类,CommonsMultipartRequestHandler和DiskMultipartRequestHandler。到底哪个是处理FormFile的上传的呢?这个问题应该从org.apache.struts.config包里来找。 
    
    org.apache.struts.config包是用来处理struts配置文件的数据的包。找到org.apache.struts.config. ControllerConfig。 

    看这几行: 

Java代码   收藏代码
  1. /**       
  2. * The fully qualified Java class name of the MultipartRequestHandler       
  3. * class to be used.       
  4. */       
  5. protected String multipartClass =   
  6. "org.apache.struts.upload.CommonsMultipartRequestHandler";    
  7.       
  8. public String getMultipartClass()   
  9. {           
  10.      return (this.multipartClass);       
  11. }        
  12.   
  13. public void setMultipartClass(String multipartClass)   
  14. {           
  15.      if (configured)   
  16.      {               
  17.            throw new IllegalStateException("Configuration is frozen");                    
  18.      }    
  19.           
  20.      this.multipartClass = multipartClass;      
  21. }    


这几行的意思很明白, 如果没有在配置文件中配置MultipartRequestHandler实现类的绝对路径,那就使用org.apache.struts.upload.CommonsMultipartRequestHandler类默认处理 。 

   ^_^,这就是关键了: struts是默认使用org.apache.struts.upload.CommonsMultipartRequestHandler类来处理FormFile指定的上传文件的。  

   马上转到org.apache.struts.upload.CommonsMultipartRequestHandler来看看: 

Java代码   收藏代码
  1. /**     
  2. *默认文件上传的大小是250M     
  3. */      
  4. public static final long DEFAULT_SIZE_MAX = 250 * 1024 * 1024;       
  5. /**     
  6. *上传文件在内存中使用的缓冲区大小,超过次数值的数据写入硬盘。     
  7. */      
  8. public static final int DEFAULT_SIZE_THRESHOLD = 256 * 1024;   


还有,最最重要的实现方法: 
Java代码   收藏代码
  1. /**     
  2. * Parses the input stream and partitions the parsed items into a set of     
  3. * form fields and a set of file items. In the process, the parsed items     
  4. * are translated from Commons FileUpload <code>FileItem</code> instances     
  5. * to Struts <code>FormFile</code> instances.  
  6. * @param request The multipart request to be processed.   
  7. * @throws ServletException if an unrecoverable error occurs.                
  8. 就是这个函数处理上传文件的request,把request交给Commons FileUpload 控件处理,并解析FileItem转换成Struts的FormFile。     
  9. */  
  10. public void handleRequest(HttpServletRequest request) throws ServletException   


再看看这个函数内部是怎么实现的吧? 

Java代码   收藏代码
  1. // 使用了DiskFileUpload。    
  2. // (Commons-FileUpload很老版本的一个上传实现类了,还在用?我的显示是Deprecated)      
  3. DiskFileUpload upload = new DiskFileUpload();      
  4. // 上传最大值      
  5. upload.setSizeMax((int) getSizeMax(ac));      
  6. // 上传文件在内存中使用的缓冲区大小      
  7. upload.setSizeThreshold((int) getSizeThreshold(ac));      
  8. // 存在硬盘的什么地方,一般是默认  
  9. pload.setRepositoryPath(getRepositoryPath(ac));    


接着看handleRequest如何处理request的: 

Java代码   收藏代码
  1. // Parse the request into file items.              
  2. List items = null;              
  3. try   
  4. {                  
  5.      items = upload.parseRequest(request);              
  6. }  
  7. //这里是关键:上传过程中出了超出最大值的异常了,如何处理?        
  8. catch (DiskFileUpload.SizeLimitExceededException e)   
  9. {                   
  10.      // Special handling for uploads that are too big        
  11.      request.setAttribute(  
  12. MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED,  
  13.            Boolean.TRUE);                  
  14.      return;              
  15. }                   
  16. //出了其他异常,如enctype不对,磁盘空间不足怎么办?      
  17. catch (FileUploadException e)   
  18. {                  
  19.      log.error("Failed to parse multipart request", e);                  
  20.      throw new ServletException(e);              
  21. }   


   这次一目了然了: 

    Struts根本没有把上传过程中出的超出最大值的异常带到Action,而是把它放到了rquest的Attribute里。  
    而出了其他异常如enctype不对,磁盘空间不足怎么办?很遗憾,Struts没有去处理它,而是log了一下,抛给了上一层了。 

    那我一定要获得这些全部异常咋办呢?没办法,自己定制一个MultipartRequestHandler吧,那样就能彻底解决上传过程中的控制问题了!  

   在此之前,我们得先去最新版的commons-fileupload控件看看上传过程中可能抛出多少异常? 
Java代码   收藏代码
  1.       
  2. //所有上传异常的父类  
  3. org.apache.commons.fileupload.FileUploadException   
  4. //注意:这个类的类名是FileUploadBase.SizeLimitExceededException是个public内  
  5. //部类。上传的formdata总的数据超出了规定大小  
  6. org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException   
  7. //注意:也是个内部类。这个才是上传的文件超出了规定大小  
  8. org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException  
  9. //其它的,也看看吧:  
  10. org.apache.commons.fileupload.FileUploadBase.FileUploadIOException  
  11. org.apache.commons.fileupload.FileUploadBase.InvalidContentTypeException  
  12. org.apache.commons.fileupload.FileUploadBase.IOFileUploadException  
  13. org.apache.commons.fileupload.FileUploadBase.UnknownSizeException    

    要想获得尽可能仔细的数据就在处理的try/catch块里把上面的异常都catch一下,放到request的attribute里去就OK了。 

    另外还有要说的是, 最好用commons-fileupload控件的最新版本,因为DiskFileUpload这个类,commons-fileupload已经弃用了,取而代之的是ServletFileUpload类了,所以一定要注意!切记,切记….. 

    这是我写的CommonsMultipartRequestHandler替代类的public void handleRequest(HttpServletRequest request) throws ServletException函数: 
Java代码   收藏代码
  1. public void handleRequest(HttpServletRequest request) throws ServletException  
  2. {  
  3.      // Get the app config for the current request.  
  4.      ModuleConfig ac = (ModuleConfig) request.getAttribute(Globals.MODULE_KEY);  
  5.   
  6.      // DiskFileItem工厂,主要用来设定上传文件的参数  
  7.      DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();  
  8.   
  9.      // 上传文件所用到的缓冲区大小,超过此缓冲区的部分将被写入到磁盘  
  10.      fileItemFactory.setSizeThreshold((int) this.getSizeThreshold(ac));  
  11.   
  12.      // 上传文件用到的临时文件存放位置  
  13.      fileItemFactory.setRepository(this.getRepository(ac));  
  14.   
  15.      // 使用fileItemFactory为参数实例化一个ServletFileUpload对象  
  16.      // 注意:该对象为commons-fileupload-1.2新增的类.  
  17.      // 对于1.2以下的commons-fileupload版本并不存在此类.  
  18.      ServletFileUpload upload = new ServletFileUpload(fileItemFactory);  
  19.   
  20.      // 从session中读取对本次上传文件的最大值的限制  
  21.      String maxUploadSize = (String)request.getSession().  
  22. getAttribute(BasicConstants.maxUploadSize);  
  23.   
  24.      // 获取struts-config文件中controller标签的maxFileSize属性来确定默认上传的限制  
  25.      // 如果struts-config文件中controller标签的maxFileSize属性没设置则使用默认的上传限制250M.  
  26.      long defaultOrConfigedMaxUploadSize = this.getSizeMax(ac);  
  27.      if (maxUploadSize != null && maxUploadSize != "")  
  28.      {  
  29.           // 如果maxUploadSize设定不正确则上传限制为defaultOrConfigedMaxUploadSize的值  
  30.   
  31.           // 正确则为maxUploadSize转换成的字节数  
  32.           upload.setSizeMax((long) this.convertSizeToBytes(  
  33. maxUploadSize, defaultOrConfigedMaxUploadSize));  
  34.         
  35.       }  
  36.       else  
  37.       {  
  38.           // 如果maxUploadSize没设置则使用默认的上传限制   
  39.           upload.setSizeMax(defaultOrConfigedMaxUploadSize);  
  40.       }   
  41.   
  42.       // 从session中清空maxUploadSize  
  43.       request.getSession().removeAttribute("maxUploadSize");   
  44.   
  45.       // Create the hash tables to be populated.  
  46.       elementsText = new Hashtable();  
  47.       elementsFile = new Hashtable();  
  48.       elementsAll = new Hashtable();   
  49.   
  50.       // Parse the request into file items.  
  51.       List items = null;  
  52.       // ServletFileUpload类来处理表单请求  
  53.       // 抛出的异常为FileUploadException的子异常  
  54.       // 如果捕获这些异常就将捕获的异常放到session中返回.  
  55.   
  56.       try  
  57.       {  
  58.             items = upload.parseRequest(request);  
  59.   
  60.       }  
  61.       catch (FileUploadBase.SizeLimitExceededException e)  
  62.       {  
  63.   
  64.             // 请求数据的size超出了规定的大小.  
  65.             request.getSession().setAttribute(  
  66.                 BasicConstants.baseSizeLimitExceededException, e);  
  67.             return;  
  68.       }  
  69.       catch (FileUploadBase.FileSizeLimitExceededException e)  
  70.       {  
  71.             // 请求文件的size超出了规定的大小.  
  72.             request.getSession().setAttribute(  
  73.                 BasicConstants.baseFileSizeLimitExceededException, e);  
  74.             return;  
  75.       }  
  76.       catch (FileUploadBase.IOFileUploadException e)  
  77.       {  
  78.             // 文件传输出现错误,例如磁盘空间不足等.  
  79.             request.getSession().setAttribute(  
  80.                 BasicConstants.baseIOFileUploadException, e);  
  81.             return;  
  82.       }  
  83.       catch (FileUploadBase.InvalidContentTypeException e)  
  84.       {  
  85.             // 无效的请求类型,即请求类型enctype != "multipart/form-data"  
  86.             request.getSession().setAttribute(  
  87.                 BasicConstants.baseInvalidContentTypeException, e);  
  88.             return;  
  89.       }  
  90.       catch (FileUploadException e)  
  91.       {  
  92.            // 如果都不是以上子异常,则抛出此总的异常,出现此异常原因无法说明.  
  93.            request.getSession().setAttribute(  
  94.                 BasicConstants.FileUploadException, e);  
  95.            return;  
  96.       }  
  97.   
  98.       // Partition the items into form fields and files.  
  99.       Iterator iter = items.iterator();  
  100.         
  101.       while (iter.hasNext())  
  102.       {  
  103.             FileItem item = (FileItem) iter.next();  
  104.   
  105.             if (item.isFormField())  
  106.             {  
  107.                    addTextParameter(request, item);  
  108.             }  
  109.             else  
  110.             {  
  111.                    addFileParameter(item);  
  112.             }  
  113.       }  
  114. }  


其它部分均未做什么大改变。 

好了,替代类写好了,我们怎么去用呢? 

这样:在struts-config文件中写配置: 
Xml代码   收藏代码
  1. <!-- ========== Controller Configuration ================================ -->  
  2.   
  3.        <controller>  
  4.   
  5.               <!-- The "input" parameter on "action" elements is the name of a  
  6.   
  7.                      local or global "forward" rather than a module-relative path -->  
  8.   
  9.               <set-property value="true" property="inputForward" />  
  10.               <set-property value="text/html; charset=UTF-8"  
  11.                      property="contentType" />  
  12.               <!-- 通过写类的全名来替代struts默认的MultipartRequestHandler -->               
  13.   
  14.               <set-property property="multipartClass"   
  15. value="com.amplesky.commonmodule.struts.AmpleskyMultipartRequestHandler" />  
  16.               <!-- 规定的上传文件的最大值 -->  
  17.               <set-property property="maxFileSize" value="15M" />  
  18.               <!--  缓冲区大小 -->  
  19.               <set-property property="memFileSize" value="5M" />  
  20.        </controller>  


好了!现在我们再用FormFile上传文件, 可以在上传之前动态设置或者从配置文件设置上传文件的大小,一旦上传过程中出现了异常,就会被写入request的attributs里。当进入action的时候,通过在Action里获取异常就可以判断上传过程中出了什么问题了, 而且在上传过程中文件一旦超出了规定大小,或者磁盘大小不足的情况会立即中断上传的。这样我们的功能就实现了。 

最后,感慨一下, 

1.感觉commons-fileupload还是挺好用的,但是FormFile的使用不大好,基本上误导能力很强,网上关于FormFile的资料说明很少,以上这些都是我自己摸索出来的。 

2.Struts 1都到了1.3版本了,但是对于FormFile的实现依然使用commons-fileupload-1.0版本的DiskFileUpload类,可见更新也不怎么样。其实我觉得这是apache大部分开源工具的一个通病:更新确实不怎么快,commons框架里边的源代码和jar包不一致,还有很多是2004或者2003年的…要命的是居然不支持javase5的新特性???? 

3. 认为把异常放到request的attributs里不好,曾经尝试过抛出异常然后通过配置exception-controller来抓去异常处理,但是抓不到,怎么都抓不到,后来翻了大半天源码才发现:struts在处理异常请求的时候将出现的ServletException和IOExcepton都交给了上层去处理了,根本不会抛出来。所以这两种异常是抓不到的。
CommonsMultipartRequestHandler.java struts-1.3.jar 文件上传核心类,linux服务器上传文件后,比较文件大小 上传文件在内存中若是超过250kb,则将缓存文件写到硬盘。 立即下载

相关推荐

高斯泼溅(Gaussian Splatting):3D建模的颠覆性革命

高斯泼溅(Gaussian Splatting):3D建模的颠覆性革命

yingtu666的博客 5149

struts1.2 文件上传处理

前一段时间刚来&lt;nobr style="border-bottom:#6600ff 1px dotted; background-color:transparent; color:#6600ff; text-decoration:underline"&gt;公司&lt;/nobr&gt;,看到一个项目中以前有人写的struts代码。是使用了FormFile来处理关于文件上传的模块。但是用力一...

移动编程 236

拯救者R7000用户实测:用华为电脑管家11.0版搞定MatePad Pro多屏协同(附驱动与显卡避坑指南)

本文详细介绍了如何在非华为笔记本联想拯救者R7000上安装华为电脑管家11.0版,实现与MatePad Pro的多屏协同功能。从驱动优化到显卡配置,提供了全面的避坑指南和实用技巧,帮助用户解决连接失败等常见问题,提升跨设备生产力体验。

weixin_30788619的博客 398

org.apache.struts.upload

这是struts1.1的org.apache.struts.upload包的描述: 从上图我们可以看出有有CommonsMultipartRequestHandler和DiskMultipartRequestHandler两个类实现了MultipartRequestHandler接口。 大家都知道,Commons-fileupload控件在上传的时候,使用的enc...

xlin 523

【Fluent】太阳辐射模型—Solar Ray Tracing

一、引言Fluent提供了一个太阳能负载模型,可用于计算进入计算域的太阳光线的辐射效应。该模型可以分为:太阳光线追踪和太阳辐照。其中太阳辐照需要使用DO或MC模型,直接向辐射模型提供外光束方向和强度参数。太阳光线追踪不限制辐射模型,可以独立于辐射方程使用,甚至可以在辐射模型off的情况下使用。本文主要介绍太阳光线追踪模型。该模型非常适用于以下领域汽车气候控制 (ACC) 应用建筑物中的人体热舒适建模应用该模型使用着色算法进行太阳光线追踪。光线会透过半透明的面加载在照射面上。

a5567899的博客 7587

用blender为模型贴图并在gazebo中显示

用blender为模型贴图并在gazebo中显示 首先我用的是最新的blender2.92版本,附上链接https://www.blender.org/download/。并设置好简体中文。语言可以在编辑->偏好设置->界面里面设置。 首先界面右上角的摄像头,方块,灯光三个场景全都删除。 接着导入stl格式的模型文件,下面以tianbot_mini上面的激光雷达的模型为例。 导入激光雷达模型后,点击进入上方的UV Editing按钮进入UV编辑界面 此时我面看见网格化的激光雷达模型

qq_48427527的博客 7999

java导入org.apache_[导入]Struts实现上传、下载(org.apache.struts.actions.DownloadAction)

StrutsFileUpload文件上传的简单范例HTMLHTML页面需要做两件事情,首先,表单需要指定enctype="multipart/form-dataand",其次需要一个类型为file的表单控件。action="/mywebapp/uploadMyFile.do"enctype="multipart/form-data">Select File: JSP上面的...

weixin_33268464的博客 817

带挂牵引车倒车运动模型推导及仿真实现

半挂牵引车在倒车时,由于鞍座主销的连接限制,其运动方式与乘用车有显著差异。例如,乘用车在右侧倒车入库时,只需确定泊车起点并将方向盘向右打死。相反,牵引车在倒车时需要先向左转方向盘,当铰接角达到防折叠的临界值时,再将方向盘向右转回,以调整牵引车与挂车至直线状态,完成泊车入库。本研究旨在建立一个基于牵引车带挂车倒车运动学模型,该模型基于以下假设: 道路在车辆行驶过程中保持平坦,无凹凸不平; 车辆以低速行驶,且在运行过程中车身不发生侧倾或俯仰; 行驶过程中,轮胎无变形,无侧向滑动,且不产

jianmo的博客 2697

Struts 文件上传 FormFile与Commons-FileUpload控件

前一段时间刚来公司,看到一个项目中以前有人写的struts代码。是使用了FormFile来处理关于文件上传的模块。但是用力一段时间后,发现出问题了。写完的这个模块,上传文件是没有问题的,但是当服务器的空间较小的时候,穿一个比较大的文件就出问题了,文件还没有上传完,就抛出一个错误的页面,报告上传模块出了问题,而且是Tomcat默认的出错页面。 于是想办法,修改,查看源代码,发现原来写...

幻影浪子 471

PCB设计AD规则设置(按照嘉立创设置)

按照嘉立创的相关加工能力进行设置: 1.Hole Size(钻孔孔径) 嘉立创要求钻孔孔径在0.2-6.3mm,并给了公差(如下图给出了钻孔孔径和公差) 规则设置位置:Manufacturing->Hole Size,这里就改一下数值,最小设置为0.2mm,最大设置为6.3mm 2.Width(线宽) 嘉立创要求线宽多层板双层板最小为3.5mil,单双层板最小为5mil(如下图给出了线宽) 规则设置位置:Routing->Width,我目前只画过双层板,按我的设置如下,最小的线宽设置为10

subtitle_的博客 4万+

【微知】关于GB200 NVL72服务器的一些信息?(36CPU72GPU;LPDDR5X 17T;HBM3e 13.5T;5代NVlink 1.8TB; 720P算力FP8;120Kw)

本文仅根据官网介绍分析,存在纰漏仅供参考。

技术札记 5382

关键字: struts、formfile、commons-fileupload、java文件上传

 关键字: struts、formfile、commons-fileupload、java文件上传 前一段时间刚来公司,看到一个项目中以前有人写的struts代码。是使用了FormFile来处理关于文件上传的模块。但是用力一段时间后,发现出问题了。写完的这个模块,上传文件是没有问题的,但是当服务器的空间较小的时候,穿一个比较大的文件就出问题了,文件还没有上传完,就抛出一个错误的页面,报告上传模

liaomin416100569的专栏 1010

Android 16强制横竖屏设置

但开发者端的响应却相对滞后,特别是中小型开发团队受限于大屏设备市场渗透率(截至2023年Q2,Android平板全球激活量约1.2亿台,不足手机设备5%)及开发成本考量,适配优先级普遍偏低。这种适配滞后已形成恶性循环——开发者因设备基数不足降低适配意愿,而用户体验缺陷又反向抑制大屏设备的市场接受度。要破解这一困局,既需要Google持续优化Jetpack Compose等现代化UI工具链,更需要建立开发者激励体系,通过应用商店流量倾斜、开发补贴等经济手段提升适配投入产出比。再不肯好好适配大屏设备?

xiangzhihong8的专栏 977

常用JSP文件上传,下载的一些方法

文件上传在web应用中非常普遍,要在jsp环境中实现文件上传功能是非常容易的,因为网上有许多用java开发的文件上传组件,下面我介绍几种有关文件上传组件。  一. 基于org.apache.struts.upload.FormFile的文件上传。这种方法相对比较简单。原理是在Actionform中定义一个FormFile类型的属性file,这个类型是struts所提供的包中的对象——“org

极致的专注 无限的热情 2885

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Struts Classic 的变迁(上),从1.0到1.1

Struts 1.0 的配置DTD 已经不赞成使用,引入了新的struts-config_1_1.dtd。在Struts 1.1 中,已有的Struts 配置文件可以使用这两个版本的DTD载入。 新的Commons 包依赖性在Struts中使用了多个Apache Jarkarta commons的组件,而Commons组件并不仅仅可以用来构建Struts应用。在Struts1.1中,所使用

mrdangdong的专栏 1485

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

上一篇: tomcat server.xml配置
下一篇: C3P0详细配置
LowKeySk
博客等级 码龄15年 21粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值