文件下载实例

这篇博客详细记录了在开发中如何实现文件下载功能,包括后台Java代码配置文件路径和文件类型,以及前端HTML中如何添加下载链接,实现点击下载的交互操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在开发工作中遇到文件下载内容,记录下来

后台代码:



import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.util.WebUtils;



@RestController
@Transactional
@RequestMapping("/upload")
public class UploadController {
	@Autowired
	public UploadService service;
	@Autowired
	public FileType fileType;

	@Value("${upload.path:{#/opt/data/upload}}")
	private String basepath;

	@Value("${file.endstr}")
	private String endstr;

	private IIdentityService ident = AdapterFactory.getIdentityService();

	@RequestMapping(value = "/test", method = RequestMethod.GET)
	public String test() {
		return "1234567890";
	}

	@RequestMapping(value = "/upload", method = { RequestMethod.GET, RequestMethod.POST })
	// @PathVariable("tableName")String tableName,@RequestParam("file")
	// MultipartFile file
	public Map<String, Object> upload(HttpServletRequest request) {

		OutputStream out = null;
		InputStream is = null;
		Map<String, Object> result = new HashMap<String, Object>();

		String tableName = request.getParameter("tableName");
		String id = request.getParameter("pkVal");
		String name = request.getParameter("colName");
		String fileTypes = request.getParameter("fileType");

		String contentType = request.getContentType();
		if (contentType == null || !contentType.toLowerCase().startsWith("multipart/")) {
			result.put("code", 500);
			result.put("msg", "文件不存在或者文件内容为空");
			return result;
		}

		MultipartHttpServletRequest multipartRequest = WebUtils.getNativeRequest(request,
				MultipartHttpServletRequest.class);
		MultipartFile file = multipartRequest.getFile("file");
		// 判断文件是否存在
		if (file.isEmpty()) {
			result.put("code", 500);
			result.put("msg", "文件不存在");
			return result;
		}
		String fileName = file.getOriginalFilename();

		String substring = fileName.substring(fileName.indexOf(".") + 1, fileName.length());

		// 自定义路径
		SimpleDateFormat fm = new SimpleDateFormat("yyyyMMdd");
		SimpleDateFormat fm2 = new SimpleDateFormat("yyyyMMddHHmmss");
		String filePath = File.separator + "upload" + File.separator + fm.format(new Date()) + File.separator
				+ fm2.format(new Date()) + "." + substring;
		File dest = new File(basepath + filePath);
		if (!dest.getParentFile().exists()) {
			dest.getParentFile().mkdirs();
		}

		try {
			byte[] bytes = file.getBytes();
			out = new FileOutputStream(dest);
			is = new ByteArrayInputStream(bytes);
			byte[] buff = new byte[1024];
			int len = 0;
			while ((len = is.read(buff)) != -1) {
				out.write(buff, 0, len);
			}
			is.close();
			out.close();
			// 验证文件类型
			fileType.getAllFileType();//加载文件类型
			String fileType = FileType.getFileType(dest);

			if (checkType(fileType.substring(fileType.indexOf("+") + 1))) {
				if (dest.delete()) {
					result.put("msg2", "文件删除成功");
				}
				;
				result.put("code", 500);
				result.put("msg", "文件类型不符");
				return result;
			}

			// 保存上传信息
			UploadFileInformation infor = new UploadFileInformation();
			infor.setMain_table(tableName);// 主记录表名
			infor.setMain_id(id);// 主记录id

			infor.setFile_name(fileName);// 文件名称
			infor.setFile_id(filePath);// 文件标识路径
			infor.setSmall_id(filePath);// 缩略图标识路径
			infor.setMain_field(name);// 主记录字段名称

			infor.setFile_type(fileTypes == null ? "图片" : fileTypes);// 文件类型
			infor.setReg_date(new Date());// 登记时间

			double size = file.getSize();
			double kilobytes = (size / 1024);
			double megabytes = (kilobytes / 1024);
			infor.setAtt_size(new BigDecimal(String.format("%.2f", megabytes)));// 文件大小

			// 人员信息 TODO
			String reg_staff_id = null;
			String reg_staff_name = null;
			String iscUserId = null;
			if (iscUserId == null) {
				iscUserId = (String) request.getAttribute("user_id");
			}
			if (null == iscUserId || ("").equals(iscUserId) || iscUserId.equals("null")) {
				reg_staff_id = "1";
				reg_staff_name = "2";
			} else {
				String userinfo[] = iscUserId.split(":");
				List<User> users = ident.getUserByIds(new String[] { userinfo[0] });
				reg_staff_name = users.get(0).getName();
				reg_staff_id = users.get(0).getId();
			}
			infor.setReg_staff_id(reg_staff_id);
			infor.setReg_staff_name(reg_staff_name);

			// infor.setATT_FILE(new Byte("99"));//数据库存储功能的存储字段
			service.uploadFileInformation(infor);

			result.put("code", 200);
			result.put("msg", "文件上传成功");
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			result.put("code", 500);
			result.put("msg", "文件上传失败");
			return result;
		}
	}

	private boolean checkType(String substring) {
		boolean open = false;
		
		if("null".equals(substring)){
			open = true;
		}
		return open;
	}

	@RequestMapping(value = "/download", method = RequestMethod.GET)
	public Map<String, Object> download(HttpServletRequest request, HttpServletResponse response,
			@RequestParam("tableName") String tableName, @RequestParam("pkVal") String pkVal,
			@RequestParam("colName") String colName) {
		UploadFileInformation infor = new UploadFileInformation();
		infor.setMain_table(tableName);
		infor.setMain_id(pkVal);
		infor.setMain_field(colName);

		Map<String, Object> result = new HashMap<String, Object>();
		result.put("code", 500);
		result.put("result", "");
		result.put("msg", "下载失败");
		infor = service.findOneByTABLE_NAMEAndPK_VALAndCOL_NAME(infor);
		if (infor == null) {
			return result;
		}
		String fileName = infor.getFile_name();
		result.put("fileName", fileName);
		File file = new File(basepath + infor.getFile_id());

		FileInputStream inputFile = null;
		String base64 = null;

		try {
			inputFile = new FileInputStream(file);
			byte[] buffer = new byte[(int) file.length()];
			byte[] bytes = new byte[inputFile.available()];
			int length = inputFile.read(bytes);
			base64 = Base64.getEncoder().encodeToString(bytes);
			result.put("code", 200);
			result.put("result", "data:text/xml;base64," + base64);
			result.put("msg", "");
			return result;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (inputFile != null) {
					inputFile.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		result.put("msg", "下载失败。。。");
		return result;
	}

	@RequestMapping(value = "/show", method = RequestMethod.GET)
	public Map<String, Object> show(HttpServletRequest request, HttpServletResponse response,
			@RequestParam("tableName") String tableName, @RequestParam("pkVal") String pkVal,
			@RequestParam("colName") String colName) {
		UploadFileInformation infor = new UploadFileInformation();
		infor.setMain_table(tableName);
		infor.setMain_id(pkVal);
		infor.setMain_field(colName);

		Map<String, Object> result = new HashMap<String, Object>();
		result.put("code", 500);
		result.put("result", "");
		result.put("msg", "预览错误");
		infor = service.findOneByTABLE_NAMEAndPK_VALAndCOL_NAME(infor);
		if (infor == null) {
			return result;
		}
		String fileName = infor.getFile_name();
		File file = new File(basepath + infor.getFile_id());

		FileInputStream inputFile = null;
		String base64 = null;

		try {
			inputFile = new FileInputStream(file);
			byte[] buffer = new byte[(int) file.length()];
			byte[] bytes = new byte[inputFile.available()];
			int length = inputFile.read(bytes);
			base64 = Base64.getEncoder().encodeToString(bytes);
			result.put("code", 200);
			result.put("result", "data:text/xml;base64," + base64);
			result.put("msg", "");
			return result;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (inputFile != null) {
					inputFile.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		result.put("msg", "预览错误。。。");
		return result;
	}

	/**
	 * 			根据id 类型 返回所有信息
	 * @param mainId  id
	 * @param type		文件类型
	 * @param isReturnFile		是否返回64   1 :返回
	 * @return
	 */
	@RequestMapping(value = "/findbymainidandtype", method = RequestMethod.GET)
	public Map<String, Object> findbymainidandtype(
			@RequestParam("mainId") String mainId,
			@RequestParam("type") String type,
			@RequestParam("isReturnFile") String isReturnFile
			) {
		List<UploadFileInformation> data = service.findbymainid(mainId,type);
		
		if("1".equals(isReturnFile)){
			for (UploadFileInformation uploadFileInformation : data) {
				String base64 = toBase64(uploadFileInformation);
				uploadFileInformation.setImg_base64(base64);
			}
		}

		Map<String, Object> result = new HashMap<String, Object>();
		result.put("code", 200);
		result.put("data", data);
		result.put("msg", "");
		return result;

	}
	/**
	 * 根据 主记录ID 查询所有的信息
	 * 
	 * @param mainId
	 * @return
	 */
	@RequestMapping(value = "/findbymainid", method = RequestMethod.GET)
	public Map<String, Object> findbymainid(
			@RequestParam("mainId") String mainId) {
		List<UploadFileInformation> data = service.findbymainid(mainId,null);
		
		for (UploadFileInformation uploadFileInformation : data) {
			String base64 = toBase64(uploadFileInformation);
			uploadFileInformation.setImg_base64(base64);
		}

		Map<String, Object> result = new HashMap<String, Object>();
		result.put("code", 200);
		result.put("data", data);
		result.put("msg", "");
		return result;

	}
	
	private String toBase64 (UploadFileInformation infor){
		File file = new File(basepath + infor.getFile_id());

		FileInputStream inputFile = null;
		String base64 = null;

		try {
			inputFile = new FileInputStream(file);
			byte[] buffer = new byte[(int) file.length()];
			byte[] bytes = new byte[inputFile.available()];
			int length = inputFile.read(bytes);
			base64 = Base64.getEncoder().encodeToString(bytes);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (inputFile != null) {
					inputFile.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		return "data:text/xml;base64,"+base64;
	}
	
	
}

FileType:



import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.util.WebUtils;

@RestController
@Transactional
@RequestMapping("/upload")
public class UploadController {
	@Autowired
	public UploadService service;
	@Autowired
	public FileType fileType;

	@Value("${upload.path:{#/opt/data/upload}}")
	private String basepath;

	@Value("${file.endstr}")
	private String endstr;

	private IIdentityService ident = AdapterFactory.getIdentityService();

	@RequestMapping(value = "/test", method = RequestMethod.GET)
	public String test() {
		return "1234567890";
	}

	@RequestMapping(value = "/upload", method = { RequestMethod.GET, RequestMethod.POST })
	// @PathVariable("tableName")String tableName,@RequestParam("file")
	// MultipartFile file
	public Map<String, Object> upload(HttpServletRequest request) {

		OutputStream out = null;
		InputStream is = null;
		Map<String, Object> result = new HashMap<String, Object>();

		String tableName = request.getParameter("tableName");
		String id = request.getParameter("pkVal");
		String name = request.getParameter("colName");
		String fileTypes = request.getParameter("fileType");

		String contentType = request.getContentType();
		if (contentType == null || !contentType.toLowerCase().startsWith("multipart/")) {
			result.put("code", 500);
			result.put("msg", "文件不存在或者文件内容为空");
			return result;
		}

		MultipartHttpServletRequest multipartRequest = WebUtils.getNativeRequest(request,
				MultipartHttpServletRequest.class);
		MultipartFile file = multipartRequest.getFile("file");
		// 判断文件是否存在
		if (file.isEmpty()) {
			result.put("code", 500);
			result.put("msg", "文件不存在");
			return result;
		}
		String fileName = file.getOriginalFilename();

		String substring = fileName.substring(fileName.indexOf(".") + 1, fileName.length());

		// 自定义路径
		SimpleDateFormat fm = new SimpleDateFormat("yyyyMMdd");
		SimpleDateFormat fm2 = new SimpleDateFormat("yyyyMMddHHmmss");
		String filePath = File.separator + "upload" + File.separator + fm.format(new Date()) + File.separator
				+ fm2.format(new Date()) + "." + substring;
		File dest = new File(basepath + filePath);
		if (!dest.getParentFile().exists()) {
			dest.getParentFile().mkdirs();
		}

		try {
			byte[] bytes = file.getBytes();
			out = new FileOutputStream(dest);
			is = new ByteArrayInputStream(bytes);
			byte[] buff = new byte[1024];
			int len = 0;
			while ((len = is.read(buff)) != -1) {
				out.write(buff, 0, len);
			}
			is.close();
			out.close();
			// 验证文件类型
			fileType.getAllFileType();//加载文件类型
			String fileType = FileType.getFileType(dest);

			if (checkType(fileType.substring(fileType.indexOf("+") + 1))) {
				if (dest.delete()) {
					result.put("msg2", "文件删除成功");
				}
				;
				result.put("code", 500);
				result.put("msg", "文件类型不符");
				return result;
			}

			// 保存上传信息
			UploadFileInformation infor = new UploadFileInformation();
			infor.setMain_table(tableName);// 主记录表名
			infor.setMain_id(id);// 主记录id

			infor.setFile_name(fileName);// 文件名称
			infor.setFile_id(filePath);// 文件标识路径
			infor.setSmall_id(filePath);// 缩略图标识路径
			infor.setMain_field(name);// 主记录字段名称

			infor.setFile_type(fileTypes == null ? "图片" : fileTypes);// 文件类型
			infor.setReg_date(new Date());// 登记时间

			double size = file.getSize();
			double kilobytes = (size / 1024);
			double megabytes = (kilobytes / 1024);
			infor.setAtt_size(new BigDecimal(String.format("%.2f", megabytes)));// 文件大小

			// 人员信息 TODO
			String reg_staff_id = null;
			String reg_staff_name = null;
			String iscUserId = null;
			if (iscUserId == null) {
				iscUserId = (String) request.getAttribute("user_id");
			}
			if (null == iscUserId || ("").equals(iscUserId) || iscUserId.equals("null")) {
				reg_staff_id = "1";
				reg_staff_name = "2";
			} else {
				String userinfo[] = iscUserId.split(":");
				List<User> users = ident.getUserByIds(new String[] { userinfo[0] });
				reg_staff_name = users.get(0).getName();
				reg_staff_id = users.get(0).getId();
			}
			infor.setReg_staff_id(reg_staff_id);
			infor.setReg_staff_name(reg_staff_name);

			// infor.setATT_FILE(new Byte("99"));//数据库存储功能的存储字段
			service.uploadFileInformation(infor);

			result.put("code", 200);
			result.put("msg", "文件上传成功");
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			result.put("code", 500);
			result.put("msg", "文件上传失败");
			return result;
		}
	}

	private boolean checkType(String substring) {
		boolean open = false;
		
		if("null".equals(substring)){
			open = true;
		}
		return open;
	}

	@RequestMapping(value = "/download", method = RequestMethod.GET)
	public Map<String, Object> download(HttpServletRequest request, HttpServletResponse response,
			@RequestParam("tableName") String tableName, @RequestParam("pkVal") String pkVal,
			@RequestParam("colName") String colName) {
		UploadFileInformation infor = new UploadFileInformation();
		infor.setMain_table(tableName);
		infor.setMain_id(pkVal);
		infor.setMain_field(colName);

		Map<String, Object> result = new HashMap<String, Object>();
		result.put("code", 500);
		result.put("result", "");
		result.put("msg", "下载失败");
		infor = service.findOneByTABLE_NAMEAndPK_VALAndCOL_NAME(infor);
		if (infor == null) {
			return result;
		}
		String fileName = infor.getFile_name();
		result.put("fileName", fileName);
		File file = new File(basepath + infor.getFile_id());

		FileInputStream inputFile = null;
		String base64 = null;

		try {
			inputFile = new FileInputStream(file);
			byte[] buffer = new byte[(int) file.length()];
			byte[] bytes = new byte[inputFile.available()];
			int length = inputFile.read(bytes);
			base64 = Base64.getEncoder().encodeToString(bytes);
			result.put("code", 200);
			result.put("result", "data:text/xml;base64," + base64);
			result.put("msg", "");
			return result;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (inputFile != null) {
					inputFile.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		result.put("msg", "下载失败。。。");
		return result;
	}

	@RequestMapping(value = "/show", method = RequestMethod.GET)
	public Map<String, Object> show(HttpServletRequest request, HttpServletResponse response,
			@RequestParam("tableName") String tableName, @RequestParam("pkVal") String pkVal,
			@RequestParam("colName") String colName) {
		UploadFileInformation infor = new UploadFileInformation();
		infor.setMain_table(tableName);
		infor.setMain_id(pkVal);
		infor.setMain_field(colName);

		Map<String, Object> result = new HashMap<String, Object>();
		result.put("code", 500);
		result.put("result", "");
		result.put("msg", "预览错误");
		infor = service.findOneByTABLE_NAMEAndPK_VALAndCOL_NAME(infor);
		if (infor == null) {
			return result;
		}
		String fileName = infor.getFile_name();
		File file = new File(basepath + infor.getFile_id());

		FileInputStream inputFile = null;
		String base64 = null;

		try {
			inputFile = new FileInputStream(file);
			byte[] buffer = new byte[(int) file.length()];
			byte[] bytes = new byte[inputFile.available()];
			int length = inputFile.read(bytes);
			base64 = Base64.getEncoder().encodeToString(bytes);
			result.put("code", 200);
			result.put("result", "data:text/xml;base64," + base64);
			result.put("msg", "");
			return result;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (inputFile != null) {
					inputFile.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		result.put("msg", "预览错误。。。");
		return result;
	}

	/**
	 * 			根据id 类型 返回所有信息
	 * @param mainId  id
	 * @param type		文件类型
	 * @param isReturnFile		是否返回64   1 :返回
	 * @return
	 */
	@RequestMapping(value = "/findbymainidandtype", method = RequestMethod.GET)
	public Map<String, Object> findbymainidandtype(
			@RequestParam("mainId") String mainId,
			@RequestParam("type") String type,
			@RequestParam("isReturnFile") String isReturnFile
			) {
		List<UploadFileInformation> data = service.findbymainid(mainId,type);
		
		if("1".equals(isReturnFile)){
			for (UploadFileInformation uploadFileInformation : data) {
				String base64 = toBase64(uploadFileInformation);
				uploadFileInformation.setImg_base64(base64);
			}
		}

		Map<String, Object> result = new HashMap<String, Object>();
		result.put("code", 200);
		result.put("data", data);
		result.put("msg", "");
		return result;

	}
	/**
	 * 根据 主记录ID 查询所有的信息
	 * 
	 * @param mainId
	 * @return
	 */
	@RequestMapping(value = "/findbymainid", method = RequestMethod.GET)
	public Map<String, Object> findbymainid(
			@RequestParam("mainId") String mainId) {
		List<UploadFileInformation> data = service.findbymainid(mainId,null);
		
		for (UploadFileInformation uploadFileInformation : data) {
			String base64 = toBase64(uploadFileInformation);
			uploadFileInformation.setImg_base64(base64);
		}

		Map<String, Object> result = new HashMap<String, Object>();
		result.put("code", 200);
		result.put("data", data);
		result.put("msg", "");
		return result;

	}
	
	private String toBase64 (UploadFileInformation infor){
		File file = new File(basepath + infor.getFile_id());

		FileInputStream inputFile = null;
		String base64 = null;

		try {
			inputFile = new FileInputStream(file);
			byte[] buffer = new byte[(int) file.length()];
			byte[] bytes = new byte[inputFile.available()];
			int length = inputFile.read(bytes);
			base64 = Base64.getEncoder().encodeToString(bytes);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (inputFile != null) {
					inputFile.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}di
		
		return "data:text/xml;base64,"+base64;
	}
	
	
}

在yml中定义文件的路径:
在这里插入图片描述
在yml中定义文件类型:
在这里插入图片描述

前端代码:

在操作列中加入文件下载,点击下载,调用函数

self.customOperations = [ {
					caption : "下载",
					click : function(item) {
						var restClient = new RESTClient();
						var requestUrl = cube.gatewayURL
								+ `/upload/download?tableName=${item.main_Table}&pkVal=${item.main_Id}&colName=${item.main_Field}`;
						restClient.get(requestUrl, function(data) {
							if (data.code == 200) {
								const blob = data.result;
								// 自定义js的a标签
								const a = document.createElement("a");
								// 将a标签隐藏
								a.style.display = "none";
								// 获取下载文件的base64路径
								a.href = data.result;
								// 文件名
								a.download = data.fileName;
								// 将a标签增加到页面上
								document.body.appendChild(a);
								// 点击事件
								a.click();
								// 使用完毕移除a标签
								document.body.removeChild(a);
							} else {
								alert("下载失败");
							}

						});
						console.log(item)
					}
				} ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值