ftp linux 服务器 麒麟_linux搭建ftp服务器

FTP,即:文件传输协议(File Transfer Protocol),基于客户端/服务器模式,默认使用20、21端口号,其

中端口20(数据端口)用于进行数据传输,端口21(命令端口)用于接受客户端发出的相关FTP命令与参

数。FTP服务器普遍部署于局域网中,具有容易搭建、方便管理的特点。而且有些FTP客户端工具还可以支

持文件的多点下载以及断点续传技术,因此FTP服务得到了广大用户的青睐。

FTP协议有以下两种工作模式:

主动模式(PORT):FTP服务器主动向客户端发起连接请求。

被动模式(PASV):FTP服务器等待客户端发起连接请求(FTP的默认工作模式)。

vsftpd是一款运行在Linux操作系统上的FTP服务程序,具有很高的安全性和传输速度。

vsftpd有以下三种认证模式:

匿名开放模式:是一种最不安全的认证模式,任何人都可以无需密码验证而直接登陆。

本地用户模式:是通过Linux系统本地的账户密码信息进行认证的模式,相较于匿名开放模式更安全,而且

配置起来简单。

虚拟用户模式:是这三种模式中最安全的一种认证模式,它需要为FTP服务单独建立用户数据库文件,虚

拟出用来进行口令验证的账户信息,而这些账户信息在服务器系统中实际上是不存在的,仅供FTP服务程

序进行认证使用。

下面以虚拟用户模式配置讲解

1.输入图中命令,若输出图中信息表示已安装vsftpd,否则输入命令:yum install -y vsftpd 进行安装

2.安装完成后

vsftpd缺省安装在/etc/vsftpd/目录中,进入该目录后如下所示

1.vsftpd.conf:为核心配置文件

2.user_list:指定允许使用vsftpd 的用户列表文件,即白名单

3.ftpusers:指定哪些用户不能访问FTP服务器,即黑名单

4.如需去掉配置文件里的注释行输入以下命令即可

mv /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf.bak

grep -v "#" /etc/vsftpd/vsftpd.conf.bak > /etc/vsftpd/vsftpd.conf

5.vsftpd.conf常用配置参数讲解

listen=  # YES: 服务以独立运行方式运行; NO: 运行在 xinetd 内。 默认为 YES

listen_address= # 服务监听地址, 如果有多个网卡, 需要将服务绑定到指定 IP 地址

listen_port= # 服务监听端口, 默认为 21

anonymous_enable= # 是否允许匿名用户访问, 默认 NO

anon_mkdir_write_enable= # 是否允许匿名用户创建文件夹, 默认 NO

anon_other_write_enable= # 是否允许匿名用户其他的写权限, 创建文件、重命名、删除文件等权限 默认为 NO

anon_upload_enable= # 是否允许匿名用户上传, 默认 NO

anon_umask= # 匿名用户上传的文件的生成掩码, 默认为077

anon_max_rate= # 匿名用户的最大传输速率, 单位为 Byte/s, 值为 0 表示不限制

anon_world_readable_only= # 是否允许匿名用户只读浏览

local_enable= # 是否支持本地用户帐号访问

write_enable= # 是否开放本地用户的写权限

local_umask= # 本地用户上传的文件的生成掩码, 默认为077

local_max_rate= # 本地用户最大的传输速率, 单位为 Byte/s,值为0表示不限制

local_root= # 本地用户登陆后的目录,默认为本地用户的主目录

chroot_local_user= # 本地用户是否可以执行 chroot, 默认为 NO

chroot_list_enable= # 是否只有指定的用户才能执行 chroot, 默认为 NO

chroot_list_file= # 当 chroot_local_user=NO 且 chroot_list_enable=YES 时,

# 只有 filename 文件内指定的用户(每行一个用户名)可以执行 chroot,

# 默认值为 /etc/vsftpd.chroot_list

userlist_enable= # 是否启用 userlist_file 白/黑名单用户列表, 默认为 NO

userlist_deny= # 当 userlist_enable=YES(即启用 userlist_file )时, 则该字段才有效。

userlist_deny=YES: userlist_file 为 黑名单, 即在该文件内的用户均不可登录, 其他用户可以登录

userlist_deny=NO: userlist_file 为 白名单, 即在该文件内的用户才可以登录, 其他用户均不可登录

userlist_file= # 黑/白名单用户列表文件(每行一个用户名)

# 是黑名单还是白名单, 根据 userlist_deny 的值决定

# 默认值为 /etc/vsftpd.user_list

ftpd_banner= # 客户端连接服务器后显示的欢迎信息

connect_timeout= # 远程客户端响应端口数据连接超时时间, 单位为秒, 默认 60

accept_connection_timeout= # 空闲的数据连接超时时间, 单位为秒, 默认 120

data_connection_timeout= # 空闲的用户会话超时时间, 单位为秒, 默认 300

max_clients= # 在独立模式运行时, 最大连接数, 0 表示无限制

max_per_ip= # 在独立模式运行时, 每 IP 的最大连接数, 0表示无限制

pasv_min_port=45000 # PASV模式最小端口

pasv_max_port=49999  # PASV模式最大端口

3.配置防火墙开放vsftpd命令端口与PASV模式下的端口

firewall-cmd --zone=public --add-port=21/tcp --permanent

firewall-cmd --zone=public --add-port=45000-49000/tcp --permanent

firewall-cmd --reload

4.创建用于FTP认证的用户数据库文件

vim /etc/vsftpd/vuser.txt

其中奇数行为用户名,偶数行为密码

如:

xuyuanyuan

123456

chendanting

123456

明文信息不安全,需要使用db_load命令用哈希(hash)算法将明文信息转换成数据文件,然后将明文信息文件删除

db_load -T -t hash -f /etc/vsftpd/vuser.txt /etc/vsftpd/vuser.db

chmod 600 /etc/vsftpd/vuser.db

rm -f /etc/vsftpd/vuser.txt

5.创建虚拟用户映射的系统本地用户和FTP根目录

输入命令:

useradd -d /ftp_data -s /sbin/nologin virtual

chmod -Rf 755 /ftp_data

6.建立用于支持虚拟用户的PAM文件

PAM(可插拔认证模块)是一种认证机制,通过一些动态链接库和统一的API把系统提供的服务与认证方式分

开,使得系统管理员可以根据需求灵活调整服务程序的不同认证方式。PAM采用了分层设计(应用程序层、

应用接口层、鉴别模块层)的思想,其结构如下图所示。

vim /etc/pam.d/vsftpd.vu

添加以下信息

auth         required    pam_userdb.so    db=/etc/vsftpd/vuser

account   required    pam_userdb.so    db=/etc/vsftpd/vuser

6.为vuser.txt里的用户配置相关参数

(1) 创建存放用户的目录

mkdir /etc/vsftpd/vusers_dir

(2) 为各用户创建文件,文件名即为用户名

vim /etc/vsftpd/vusers_dir/xuyuanyuan

vim /etc/vsftpd/vusers_dir/chendanting

(3) 按各用户需要加入参数配置

local_root=/ftp_data/xuyuanyuan

anon_upload_enable=YES

anon_mkdir_write_enable=YES

anon_other_write_enable=YES

anon_world_readable_only=YES

6.修改核心配置文件vsftpd.conf为以下内容

anonymous_enable=NO

local_enable=YES

# 开启虚拟用户模式

guest_enable=YES

# 指定虚拟用户对应的系统用户

guest_username=virtual

# 允许对ftp根目录执行写入操作

allow_writeable_chroot=YES

write_enable=YES

local_umask=022

dirmessage_enable=YES

xferlog_enable=YES

connect_from_port_20=YES

xferlog_std_format=YES

listen_port=21

listen=NO

listen_ipv6=YES

#PAM文件

pam_service_name=vsftpd.vu

userlist_enable=YES

tcp_wrappers=YES

# 虚拟用户配置文件目录

user_config_dir=/etc/vsftpd/vuser_dir

# 被动模式端口范围

pasv_min_port=45000

pasv_max_port=49000

chroot_local_user=YES

7.最后启动服务即可访问

systemctl start vsftpd.serivce 启动服务

systemctl stop vsftpd.serivce 停止服务

systemctl status vsftpd.serivce 查看服务状态

systemctl restart vsftpd.serivce 查看服务状态

在浏览器输入 ftp://ip:21 就可访问了

最后配上操作ftp的工具类

maven依赖

commons-net

commons-net

3.6

1 /**

2 *@authorMedusa3 * @Date 12-14-194 * @Description ftp工具类5 */

6 public classFtpUtil {7

8 /**

9 * ftp服务器的ip10 */

11 privateString ip;12

13 /**

14 * ftp服务器的端口,缺省为2115 */

16 private intport;17

18 /**

19 * 连接ftp服务器的用户名20 */

21 privateString userName;22

23 /**

24 * 用户名对应通行证25 */

26 privateString password;27

28 /**

29 * 客户端编码30 */

31 private String clientCharset = "GBK";32

33 /**

34 * 服务端编码,缺省为ISO8859-135 */

36 private String serverCharset = "ISO8859-1";37

38 privateFTPClient ftpClient;39

40 public FtpUtil(String ip, intport, String userName, String password) {41 this.ip =ip;42 this.port =port;43 this.userName =userName;44 this.password =password;45 }46

47 /**

48 * 建立连接并登陆服务器49 */

50 public booleanconnectFtp() {51 ftpClient = newFTPClient();52

53 try{54 ftpClient.connect(ip, port);55

56 //是否连接成功

57 if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {58 System.out.println("create connect fail");59 ftpClient.disconnect();60 return false;61 }62

63 if (!ftpClient.login(userName, password)) {64 System.out.println("login server fail");65 ftpClient.disconnect();66 return false;67 }68

69 //开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则使用本地编码

70 if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {71 clientCharset = "UTF-8";72 }73 ftpClient.setControlEncoding(clientCharset);74 ftpClient.enterLocalPassiveMode(); //设置被动模式

75 ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); //设置文件传输类型

76 } catch(IOException e) {77 System.out.println("create connect or close connect error");78 return false;79 }80 return true;81 }82

83 /**

84 * 退出登陆并关闭连接85 */

86 public booleancloseFtp() {87 if (ftpClient != null &&ftpClient.isConnected()) {88 try{89 ftpClient.logout();90 ftpClient.disconnect();91 return true;92 } catch(IOException e) {93 System.out.println("logout or close connect error");94 return false;95 }96 }97 return false;98 }99

100 /**

101 * 客户端转服务端编码102 */

103 privateString clientCharsetToServer(String path) {104 try{105 return newString(path.getBytes(clientCharset), serverCharset);106 } catch(UnsupportedEncodingException e) {107 System.out.println("client to server encode error");108 return null;109 }110 }111

112 /**

113 * 服务端转客户端编码114 */

115 privateString serverCharsetToClient(String path) {116 try{117 return newString(path.getBytes(serverCharset), clientCharset);118 } catch(UnsupportedEncodingException e) {119 System.out.println("server to client encode error");120 return null;121 }122 }123

124 /**

125 * 获取当前工作目录126 */

127 privateString getWorkDirectory() {128 try{129 returnserverCharsetToClient(ftpClient.printWorkingDirectory());130 } catch(IOException e) {131 System.out.println("get work directory error");132 return null;133 }134 }135

136 /**

137 * 获取当前工作目录下的所有文件名138 */

139 privateString[] getWorkDirectoryFileNames() {140 try{141 FTPFile[] ftpFiles =ftpClient.listFiles();142 if (ftpFiles.length > 0) {143 String[] fileNames = newString[ftpFiles.length];144 int i = 0;145

146 for (FTPFile ftpFile: ftpFiles) fileNames[i++] =ftpFile.getName();147

148 returnfileNames;149 }150 return null;151 } catch(IOException e) {152 System.out.println("get work directory files error");153 return null;154 }155 }156

157 /**

158 * 根据从根目录开始的完整文件路径,切换工作目录159 */

160 private booleanchangeWorkDirectory(String path) {161 try{162 returnftpClient.changeWorkingDirectory(clientCharsetToServer(path));163 } catch(IOException e) {164 System.out.println("change directory error");165 return false;166 }167 }168

169 /**

170 * 当前工作目录下是否有查询的文件171 */

172 private booleanisWorkDirectoryHasFile(String fileName) {173 try{174 FTPFile[] ftpFiles =ftpClient.listFiles();175

176 for(FTPFile ftpFile : ftpFiles) {177 if (ftpFile.getName().equals(fileName)) return true;178 }179

180 return false;181 } catch(IOException e) {182 System.out.println("get work directory files error");183 return false;184 }185 }186

187 /**

188 *获取对应路径下的文件(根目录除外),并切换工作目录至此文件的父目录189 */

190 privateFTPFile getFTPFile(String path) {191 if (path.equals("/")) {192 System.out.println("invalid root directory");193 return null;194 }195

196 String fileName =path;197 if (!path.equals("/") && path.indexOf("/") != -1) {198 String[] split = path.split("/");199 fileName = split[split.length - 1];200 }201 if (changeWorkDirectory(path.substring(0, path.length() -fileName.length()))) {202 try{203 for(FTPFile ftpFile : ftpClient.listFiles()) {204 if (ftpFile.getName().equals(fileName)) returnftpFile;205 }206 } catch(IOException e) {207 System.out.println("get work directory files error");208 return null;209 }210 }211 return null;212 }213

214 /**

215 * 获取服务端文件夹下所有文件路径216 */

217 public List> getDirectoryUnderAllFile(String path) throwsIOException {218 //存放目录和文件的路径

219 List> directoryAndFilePaths = new ArrayList<>();220 //存放目录的路径

221 List directoryPaths = new ArrayList<>();222 //存放文件的路径

223 List filePaths = new ArrayList<>();224 //待遍历处理的文件夹路径

225 Queue ergodicDirPaths = new LinkedList<>();226 ergodicDirPaths.offer(path);227 directoryPaths.add(path);228

229 while (ergodicDirPaths.size() > 0) {230 String entry =ergodicDirPaths.poll();231 if(changeWorkDirectory(entry)) {232 entry = "/".endsWith(entry) ? "": entry;233 FTPFile[] ftpFiles =ftpClient.listFiles();234 for(FTPFile ftpFile : ftpFiles) {235 if(ftpFile.isFile()) {236 filePaths.add(entry + "/" +ftpFile.getName());237 } else{238 directoryPaths.add(entry + "/" +ftpFile.getName());239 ergodicDirPaths.offer(entry + "/" +ftpFile.getName());240 }241 }242 } else{243 System.out.println("切换目录失败");244 return null;245 }246 }247

248 directoryAndFilePaths.add(directoryPaths);249 directoryAndFilePaths.add(filePaths);250 returndirectoryAndFilePaths;251 }252

253 /**

254 * 获取客户端文件夹下所有文件路径255 */

256 public List>getDirectoryUnderAllFile1(String path) {257 //存放目录和文件的路径

258 List> directoryAndFilePaths = new ArrayList<>();259 //存放目录的路径

260 List directoryPaths = new ArrayList<>();261 //存放文件的路径

262 List filePaths = new ArrayList<>();263 //待遍历处理的文件夹路径

264 Queue ergodicDirPaths = new LinkedList<>();265 ergodicDirPaths.offer(path);266 directoryPaths.add(path);267

268 while (ergodicDirPaths.size() > 0) {269 String entry =ergodicDirPaths.poll();270

271 File dir = newFile(entry);272 for(File file : dir.listFiles()) {273 if(file.isFile()) {274 filePaths.add(entry + "/" +file.getName());275 } else{276 directoryPaths.add(entry + "/" +file.getName());277 ergodicDirPaths.offer(entry + "/" +file.getName());278 }279 }280 }281

282 directoryAndFilePaths.add(directoryPaths);283 directoryAndFilePaths.add(filePaths);284 returndirectoryAndFilePaths;285 }286

287 /**

288 * 下载文件,若客户端已存在则覆盖它289 */

290 public booleandownload(String serverPath, String clientPath) {291 //判断服务端文件是否存在

292 FTPFile ftpFile =getFTPFile(serverPath);293 if (ftpFile == null) {294 System.out.println("server file not exist");295 return false;296 }297

298 //若客户端目录不存在,就创建它,否则清空目录

299 File clientDirectory = newFile(clientPath);300 if (!clientDirectory.exists()) {301 if (!clientDirectory.mkdirs()) {302 System.out.println("client create directory fail");303 return false;304 }305 } else{306 if (!clientDirectory.isDirectory()) {307 System.out.println("clientPath is not directory");308 return false;309 } else{310 if (!emptyDirectory(clientDirectory)) {311 System.out.println("empty client directory fail");312 return false;313 }314 }315 }316

317 if(ftpFile.isFile()) {318 executeDownload(serverPath, clientPath + serverPath.substring(serverPath.lastIndexOf("/")));319 } else{320 try{321 List> allFile =getDirectoryUnderAllFile(serverPath);322 //获取所有目录路径

323 List directoryPaths = allFile.get(0);324 if (directoryPaths.size() > 0) {325 directoryPaths.forEach(path -> new File(clientPath +path).mkdir());326 } else{327 System.out.println("get all directory fail");328 return false;329 }330

331 //获取所有文件路径

332 List filePaths = allFile.get(1);333 for(String path : filePaths) {334 executeDownload(path, clientPath +path);335 }336 } catch(IOException e) {337 System.out.println("error getting all files under this path");338 return false;339 }340 }341 return true;342 }343

344 /**

345 * 执行下载文件346 */

347 private voidexecuteDownload(String serverPath, String clientPath) {348 InputStream is = null;349 OutputStream os = null;350 try{351 is =ftpClient.retrieveFileStream(clientCharsetToServer(serverPath));352 File file = newFile(clientPath);353 if (!file.createNewFile()) {354 System.out.println("client create file fail");355 return;356 }357 os = newFileOutputStream(file);358 byte[] by = new byte[1024];359 int len = 0;360 while ((len = is.read(by)) != -1) os.write(by, 0, len);361 } catch(IOException e) {362 System.out.println("stream handle file fail");363 return;364 } finally{365 try{366 if (is != null) {367 is.close();368 //必须调用此方法,否则第二次调用retrieveFileStream()方法时,返回null

369 ftpClient.completePendingCommand();370 }371 if (os != null) os.close();372 } catch(IOException e) {373 e.printStackTrace();374 }375 }376 }377

378 /**

379 * 执行上传文件380 */

381 private voidexecuteUpload(String serverPath, String clientPath) {382 InputStream is = null;383 OutputStream os = null;384 try{385 is = newFileInputStream(clientPath);386 os =ftpClient.storeFileStream(clientCharsetToServer(serverPath));387

388 byte[] by = new byte[1024];389 int len = 0;390 while ((len = is.read(by)) != -1) os.write(by, 0, len);391 } catch(FileNotFoundException e) {392 System.out.println("client file not found");393 return;394 } catch(IOException e) {395 System.out.println("stream handle file fail");396 return;397 } finally{398 try{399 if (is != null) is.close();400 if (os != null) {401 os.close();402 ftpClient.completePendingCommand();403 }404 } catch(IOException e) {405 e.printStackTrace();406 }407 }408 }409

410 /**

411 * 上传文件,若服务端已存在则覆盖它412 */

413 public booleanupload(String serverPath, String clientPath) {414 File clientFile = newFile(clientPath);415 if (!clientFile.exists()) {416 System.out.println("client file not exist");417 return false;418 }419

420 if (!"/".equals(serverPath)) {421 FTPFile ftpFile =getFTPFile(serverPath);422 if (ftpFile == null) {423 String[] split = serverPath.split("/");424 changeWorkDirectory("/");425 try{426 for (int i = 1; i < split.length; i++) {427 ftpClient.makeDirectory(clientCharsetToServer(split[i]));428 changeWorkDirectory(getWorkDirectory() + "/" +split[i]);429 }430 } catch(IOException e) {431 System.out.println("create server directory error");432 return false;433 }434 }435 }436

437 serverPath = "/".endsWith(serverPath) ? "": serverPath;438 if(clientFile.isFile()) {439 try{440 String filePath = serverPath + clientPath.substring(clientPath.lastIndexOf("/"));441 ftpClient.deleteFile(filePath);442 executeUpload(filePath, clientPath);443 } catch(IOException e) {444 System.out.println("delete server file error");445 return false;446 }447 } else{448 List> allFile =getDirectoryUnderAllFile1(clientPath);449 List directoryPaths = allFile.get(0);450 String path2;451 if (directoryPaths.size() > 0) {452 try{453 String firstDir = directoryPaths.get(0);454 String path1 = firstDir.substring(firstDir.lastIndexOf("/"));455 path2 = firstDir.substring(0, firstDir.lastIndexOf("/"));456 ftpClient.makeDirectory(clientCharsetToServer(serverPath +path1));457 for (int i = 1; i < directoryPaths.size(); i++) {458 String tempPath =directoryPaths.get(i).substring(path2.length());459 ftpClient.makeDirectory(clientCharsetToServer(serverPath +tempPath));460 }461 } catch(IOException e) {462 System.out.println("create server directory error");463 return false;464 }465 } else{466 System.out.println("get all directory fail");467 return false;468 }469 List filePaths = allFile.get(1);470 for(String path : filePaths) {471 String tempPath =path.substring(path2.length());472 executeUpload(serverPath +tempPath, path);473 }474 }475 return true;476 }477

478 /**

479 * 清空目录480 */

481 private booleanemptyDirectory(File dir) {482 File[] oneLevelFile =dir.listFiles();483 if (oneLevelFile.length > 0) {484 Stack willDeleteFile = new Stack<>();485 for (int i = 0; i < oneLevelFile.length; i++) {486 willDeleteFile.push(oneLevelFile[i]);487 }488

489 while (willDeleteFile.size() > 0) {490 File entry =willDeleteFile.pop();491 if(entry.isFile()) {492 if (!entry.delete()) return false;493 } else{494 File[] lowerLevelFile =entry.listFiles();495 if (lowerLevelFile.length > 0) {496 willDeleteFile.push(entry);497 for (int i = 0; i < lowerLevelFile.length; i++) {498 willDeleteFile.push(lowerLevelFile[i]);499 }500 } else{501 if (!entry.delete()) return false;502 }503 }504 }505 }506 return true;507 }508

509 /**

510 * web端上传511 */

512 public booleanwebUpload(MultipartFile file, String serverPath) {513 if (!"/".equals(serverPath)) {514 FTPFile ftpFile =getFTPFile(serverPath);515 if (ftpFile == null) {516 String[] split = serverPath.split("/");517 changeWorkDirectory("/");518 try{519 for (int i = 1; i < split.length; i++) {520 ftpClient.makeDirectory(clientCharsetToServer(split[i]));521 changeWorkDirectory(getWorkDirectory() + "/" +split[i]);522 }523 } catch(IOException e) {524 System.out.println("create server directory error");525 return false;526 }527 }528 }529 String filePath;530 try{531 serverPath = "/".endsWith(serverPath) ? "": serverPath;532 filePath = serverPath + "/" +file.getOriginalFilename();533 ftpClient.deleteFile(filePath);534 } catch(IOException e) {535 System.out.println("delete server file error");536 return false;537 }538

539 InputStream is = null;540 OutputStream os = null;541 try{542 is =file.getInputStream();543 os =ftpClient.storeFileStream(clientCharsetToServer(filePath));544

545 byte[] by = new byte[1024];546 int len = 0;547 while ((len = is.read(by)) != -1) os.write(by, 0, len);548 } catch(FileNotFoundException e) {549 System.out.println("client file not found");550 return false;551 } catch(IOException e) {552 System.out.println("stream handle file fail");553 return false;554 } finally{555 try{556 if (is != null) is.close();557 if (os != null) {558 os.close();559 ftpClient.completePendingCommand();560 }561 } catch(IOException e) {562 e.printStackTrace();563 }564 }565 return true;566 }567

568 /**

569 * web端下载570 */

571 public booleanwebDownload(HttpServletResponse response, String serverPath) {572 FTPFile ftpFile =getFTPFile(serverPath);573 if (ftpFile == null) {574 System.out.println("server file not exist");575 return false;576 }577

578 InputStream is = null;579 try{580 is =ftpClient.retrieveFileStream(clientCharsetToServer(serverPath));581 ServletOutputStream sos =response.getOutputStream();582 byte[] by = new byte[1024];583 int len = 0;584 while ((len = is.read(by)) != -1) sos.write(by, 0, len);585 } catch(IOException e) {586 System.out.println("stream handle file fail");587 return false;588 } finally{589 try{590 if (is != null) {591 is.close();592 ftpClient.completePendingCommand();593 }594 } catch(IOException e) {595 e.printStackTrace();596 }597 }598

599 return true;600 }601

602 }

View Code

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值