酷下载
win7
win8
win10
浏览器
系统百科
特色栏目
时间:2018-10-30 来源:酷下载 作者:法老叔叔
ftp并发上传失败是一个比较麻烦的问题,十几个同时上传或者下载文件会出现限制,小编今天给大家带来了详细的解决方法,需要的快点来看看吧。
一
首先需要一个结构体
这个结构体应当拥有
(1)源文件的句柄
(2)目标文件的句柄
(3)写文件的起始位置
(4)写文件的终止位置
二
每一个结构体代表了文件的一部分
如果说把文件分成了5份,那就应该定义5个结构体,然后求出文件的大小,把文件的大小分成五份,
然后记录这一部分应当从文件的何处拷贝到何处,以及目标文件和源文件的句柄
三
开始定义线程内的函数,通过lseek函数找到应当找到的位置
然后开始拷贝就好了
package com.ourpalm.resupgrade.util.ftp;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.UnknownHostException;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import com.ourpalm.resupgrade.config.FtpConfig;
/**
* FTP 连接
* @author chuer
* @date 2015年1月7日 下午2:19:48
*/
public class FtpConnection {
public static final String ANONYMOUS_LOGIN = "anonymous";
private FTPClient ftp = new FTPClient();
private boolean is_connected = false;
* 构造函数
public FtpConnection(){
is_connected = false;
ftp.setDefaultTimeout(FtpConfig.defaultTimeoutSecond * 1000);
ftp.setConnectTimeout(FtpConfig.connectTimeoutSecond * 1000);
ftp.setDataTimeout(FtpConfig.dataTimeoutSecond * 1000);
try {
initConnect(FtpConfig.host,FtpConfig.port,FtpConfig.user,FtpConfig.password);
} catch (IOException e) {
e.printStackTrace();
}
* 初始化连接
* @param host
* @param port
* @param user
* @param password
* @throws IOException
private void initConnect(String host, int port, String user, String password) throws IOException {
ftp.connect(host, port);
} catch (UnknownHostException ex) {
throw new IOException("Can't find FTP server '" + host + "'");
int reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
disconnect();
throw new IOException("Can't connect to server '" + host + "'");
if (user == "") {
user = ANONYMOUS_LOGIN;
if (!ftp.login(user, password)) {
throw new IOException("Can't login to server '" + host + "'");
} else {
is_connected = true;
* 上传文件
* @param path
* @param ftpFileName
* @param localFile
public void upload(String path,String ftpFileName, File localFile) throws IOException {
//检查本地文件是否存在
if (!localFile.exists()) {
throw new IOException("Can't upload '" + localFile.getAbsolutePath() + "'. This file doesn't exist.");
//设置工作路径
setWorkingDirectory(path);
//上传
InputStream in = null;
//被动模式
ftp.enterLocalPassiveMode();
in = new BufferedInputStream(new FileInputStream(localFile));
//保存文件
if (!ftp.storeFile(ftpFileName, in)) {
throw new IOException("Can't upload file '" + ftpFileName + "' to FTP server. Check FTP permissions and path.");
} finally {
in.close();
} catch (IOException ex) {
* 关闭连接
public void disconnect() throws IOException {
if (ftp.isConnected()) {
ftp.logout();
ftp.disconnect();
ex.printStackTrace();
* 设置工作路径
* @param dir
* @return
private boolean setWorkingDirectory(String dir) {
if (!is_connected) {
return false;
//如果目录不存在创建目录
if(createDirecroty(dir)){
return ftp.changeWorkingDirectory(dir);
* 是否连接
public boolean isConnected(){
return is_connected;
* 创建目录
* @param remote
private boolean createDirecroty(String remote) throws IOException {
boolean success = true;
String directory = remote.substring(0, remote.lastIndexOf("/") + 1);
// 如果远程目录不存在,则递归创建远程服务器目录
if (!directory.equalsIgnoreCase("/") && !ftp.changeWorkingDirectory(new String(directory))) {
int start = 0;
int end = 0;
if (directory.startsWith("/")) {
start = 1;
start = 0;
end = directory.indexOf("/", start);
while (true) {
String subDirectory = new String(remote.substring(start, end));
if (!ftp.changeWorkingDirectory(subDirectory)) {
if (ftp.makeDirectory(subDirectory)) {
ftp.changeWorkingDirectory(subDirectory);
System.out.println("mack directory error :/"+subDirectory);
start = end + 1;
// 检查所有目录是否创建完毕
if (end <= start) {
break;
return success;
import java.util.concurrent.ArrayBlockingQueue;
* 连接工厂
* @date 2015年1月7日 下午2:32:22
public class FtpFactory {
private final ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(FtpConfig.ftpConnectionSize);
protected FtpFactory(){
System.out.println("init FtpFactory");
for(int i=0;i< FtpConfig.ftpConnectionSize; i++){
arrayBlockingQueue.offer(new FtpConnection());
* 获取连接
public FtpConnection getFtp(){
FtpConnection poll = null;
poll = arrayBlockingQueue.take();
} catch (InterruptedException e) {
return poll;
* 释放连接
* @param ftp
public boolean relase(FtpConnection ftp){
return arrayBlockingQueue.offer(ftp);
* 删除连接
public void remove(FtpConnection ftp){
arrayBlockingQueue.remove(ftp);
public void close(){
for(FtpConnection connection : arrayBlockingQueue){
connection.disconnect();
import java.util.concurrent.Callable;
import com.ourpalm.resupgrade.util.log.LoggerUtils;
* 上传任务
* @date 2015年1月7日 下午2:30:46
public class UploadTask implements Callable{
private File file;
private FtpConnection ftp;
private String path;
private String fileName;
private FtpFactory factory;
public UploadTask(FtpFactory factory,FtpConnection ftp,File file,String path,String fileName){
this.factory = factory;
this.ftp = ftp;
this.file = file;
this.path = path;
this.fileName = fileName;
@Override
public UploadResult call() throws Exception {
UploadResult result = null;
try{
if(ftp == null){
result = new UploadResult(file.getAbsolutePath(),false);
return result;
//如果连接未开启 重新获取连接
if(!ftp.isConnected()){
factory.remove(ftp);
ftp = new FtpConnection();
// factory.relase(ftp);
//开始上传
LoggerUtils.upload.info(file.getName()+" is uploading ...");
FtpResult.resultList.add(file.getName()+" is uploading ...");
ftp.upload(path, fileName, file);
result = new UploadResult(file.getName(),true);
}catch(IOException ex){
result = new UploadResult(file.getName(),false);
}finally{
factory.relase(ftp);//释放连接
FtpResult.resultList.add(result.toString());
LoggerUtils.upload.info(result.toString());
* 上传结果
* @date 2015年1月7日 下午2:31:14
public class UploadResult {
private String fileName; //文件名称
private boolean result; //是否上传成功
public UploadResult(String fileName,boolean result){
this.result = result;
public String getFileName() {
return fileName;
public void setFileName(String fileName) {
public boolean isResult() {
public void setResult(boolean result) {
public String toString(){
return "[fileName="+fileName+" , result="+result+"]";
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
* 实时上传结果
* @date 2015年1月7日 下午4:57:24
public class FtpResult {
public static List resultList = new CopyOnWriteArrayList<>();
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
* ftp上传工具包
* @date 2015年1月7日 下午2:31:39
public class FtpUtil {
* @param ftpPath
* @param listFiles
public static synchronized List upload(String ftpPath,File [] listFiles) {
ExecutorService newFixedThreadPool = Executors .newFixedThreadPool(FtpConfig.threadPoolSize);
List> results = new ArrayList<>();
FtpFactory factory = new FtpFactory();
for (File file : listFiles) {
FtpConnection ftp = factory.getFtp();
UploadTask upload = new UploadTask(factory,ftp, file, ftpPath, file.getName());
Future submit = newFixedThreadPool.submit(upload);
results.add(submit);
List listResults = new ArrayList<>();
for (Future result : results) {
UploadResult uploadResult = result.get(30, TimeUnit.MINUTES);
listResults.add(uploadResult);
} catch (Exception e) {
factory.close();
newFixedThreadPool.shutdown();
return listResults;
package com.ourpalm.resupgrade.config;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.ourpalm.resupgrade.util.io.XmlUtils;
* ftp 配置类
* @date 2015年1月7日 下午4:36:50
public class FtpConfig {
public static int defaultTimeoutSecond;
public static int connectTimeoutSecond;
public static int dataTimeoutSecond;
public static String host;
public static int port;
public static String user;
public static String password;
public static int threadPoolSize;
public static int ftpConnectionSize;
public static String rootPath;
public static void load(String path) {
path += "ftpConfig.xml";
Document doc = XmlUtils.load(path);
Element root = doc.getDocumentElement();
defaultTimeoutSecond = Integer.parseInt(XmlUtils.getChildText(root, "defaultTimeoutSecond"));
connectTimeoutSecond = Integer.parseInt(XmlUtils.getChildText(root, "connectTimeoutSecond"));
dataTimeoutSecond = Integer.parseInt(XmlUtils.getChildText(root, "dataTimeoutSecond"));
host = XmlUtils.getChildText(root, "host");
port = Integer.parseInt(XmlUtils.getChildText(root, "port"));
user = XmlUtils.getChildText(root, "user");
password = XmlUtils.getChildText(root, "password");
threadPoolSize = Integer.parseInt(XmlUtils.getChildText(root, "threadPoolSize"));
ftpConnectionSize = Integer.parseInt(XmlUtils.getChildText(root, "ftpConnectionSize"));
rootPath = XmlUtils.getChildText(root, "rootPath");
}catch(Exception e){
* 客户端
* @date 2015年1月7日 下午2:32:41
public class Client {
public static void main(String[] args) throws IOException {
String loalPath = "D:/resource/";
String ftpPath = "/resource";
File parentFile = new File(loalPath);
List resultLists = FtpUtil.upload(ftpPath,parentFile.listFiles());
for(UploadResult result : resultLists){
System.out.println(result);
雨林木风Ghost Win10 x64 专业版v2019.08 4768.37M | 2024-11-15
番茄花园Ghost Win10 64位 纯净版v2019.08 4099.14M | 2024-11-15
新萝卜家园Ghost Win10 64位 纯净版v2019.08 4099.14M | 2024-11-15
萝卜家园GHOST WIN10 64位 专业版 v2019.08 4204.78M | 2024-11-15
萝卜家园GHOST WIN10 86位 专业版 v2019.08 3098.34M | 2024-11-15
深度技术Ghost Win10 x86 纯净版v2019.08 3889.56M | 2024-11-15
Win8 32位 纯净企业版 2477.17M | 2024-11-15
Win8 32位 纯净专业版 2474.92M | 2024-11-15
GHOST win8.1 64位专业版v2018.11 3887.19M | 2024-11-15
深度技术Ghost win7 x86 特别旗舰版v2019.08 3682.63M | 2024-11-15
类似植物大战僵尸游戏大全2024
免费唱歌软件大全2024
双人逃跑游戏有哪些2024
养鱼游戏大全2024
ftp并发上传失败解决方法
时间:2018-10-30 来源:酷下载 作者:法老叔叔
ftp并发上传失败是一个比较麻烦的问题,十几个同时上传或者下载文件会出现限制,小编今天给大家带来了详细的解决方法,需要的快点来看看吧。
ftp并发上传失败解决方法:
一
首先需要一个结构体
这个结构体应当拥有
(1)源文件的句柄
(2)目标文件的句柄
(3)写文件的起始位置
(4)写文件的终止位置
二
每一个结构体代表了文件的一部分
如果说把文件分成了5份,那就应该定义5个结构体,然后求出文件的大小,把文件的大小分成五份,
然后记录这一部分应当从文件的何处拷贝到何处,以及目标文件和源文件的句柄
三
开始定义线程内的函数,通过lseek函数找到应当找到的位置
然后开始拷贝就好了
package com.ourpalm.resupgrade.util.ftp;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.UnknownHostException;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import com.ourpalm.resupgrade.config.FtpConfig;
/**
* FTP 连接
* @author chuer
* @date 2015年1月7日 下午2:19:48
*/
public class FtpConnection {
public static final String ANONYMOUS_LOGIN = "anonymous";
private FTPClient ftp = new FTPClient();
private boolean is_connected = false;
/**
* 构造函数
*/
public FtpConnection(){
is_connected = false;
ftp.setDefaultTimeout(FtpConfig.defaultTimeoutSecond * 1000);
ftp.setConnectTimeout(FtpConfig.connectTimeoutSecond * 1000);
ftp.setDataTimeout(FtpConfig.dataTimeoutSecond * 1000);
try {
initConnect(FtpConfig.host,FtpConfig.port,FtpConfig.user,FtpConfig.password);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 初始化连接
* @param host
* @param port
* @param user
* @param password
* @throws IOException
*/
private void initConnect(String host, int port, String user, String password) throws IOException {
try {
ftp.connect(host, port);
} catch (UnknownHostException ex) {
throw new IOException("Can't find FTP server '" + host + "'");
}
int reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
disconnect();
throw new IOException("Can't connect to server '" + host + "'");
}
if (user == "") {
user = ANONYMOUS_LOGIN;
}
if (!ftp.login(user, password)) {
is_connected = false;
disconnect();
throw new IOException("Can't login to server '" + host + "'");
} else {
is_connected = true;
}
}
/**
* 上传文件
* @param path
* @param ftpFileName
* @param localFile
* @throws IOException
*/
public void upload(String path,String ftpFileName, File localFile) throws IOException {
//检查本地文件是否存在
if (!localFile.exists()) {
throw new IOException("Can't upload '" + localFile.getAbsolutePath() + "'. This file doesn't exist.");
}
//设置工作路径
setWorkingDirectory(path);
//上传
InputStream in = null;
try {
//被动模式
ftp.enterLocalPassiveMode();
in = new BufferedInputStream(new FileInputStream(localFile));
//保存文件
if (!ftp.storeFile(ftpFileName, in)) {
throw new IOException("Can't upload file '" + ftpFileName + "' to FTP server. Check FTP permissions and path.");
}
} finally {
try {
in.close();
} catch (IOException ex) {
}
}
}
/**
* 关闭连接
* @throws IOException
*/
public void disconnect() throws IOException {
if (ftp.isConnected()) {
try {
ftp.logout();
ftp.disconnect();
is_connected = false;
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
/**
* 设置工作路径
* @param dir
* @return
*/
private boolean setWorkingDirectory(String dir) {
if (!is_connected) {
return false;
}
//如果目录不存在创建目录
try {
if(createDirecroty(dir)){
return ftp.changeWorkingDirectory(dir);
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* 是否连接
* @return
*/
public boolean isConnected(){
return is_connected;
}
/**
* 创建目录
* @param remote
* @return
* @throws IOException
*/
private boolean createDirecroty(String remote) throws IOException {
boolean success = true;
String directory = remote.substring(0, remote.lastIndexOf("/") + 1);
// 如果远程目录不存在,则递归创建远程服务器目录
if (!directory.equalsIgnoreCase("/") && !ftp.changeWorkingDirectory(new String(directory))) {
int start = 0;
int end = 0;
if (directory.startsWith("/")) {
start = 1;
} else {
start = 0;
}
end = directory.indexOf("/", start);
while (true) {
String subDirectory = new String(remote.substring(start, end));
if (!ftp.changeWorkingDirectory(subDirectory)) {
if (ftp.makeDirectory(subDirectory)) {
ftp.changeWorkingDirectory(subDirectory);
} else {
System.out.println("mack directory error :/"+subDirectory);
return false;
}
}
start = end + 1;
end = directory.indexOf("/", start);
// 检查所有目录是否创建完毕
if (end <= start) {
break;
}
}
}
return success;
}
}
package com.ourpalm.resupgrade.util.ftp;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import com.ourpalm.resupgrade.config.FtpConfig;
/**
* 连接工厂
* @author chuer
* @date 2015年1月7日 下午2:32:22
*/
public class FtpFactory {
private final ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(FtpConfig.ftpConnectionSize);
protected FtpFactory(){
System.out.println("init FtpFactory");
for(int i=0;i< FtpConfig.ftpConnectionSize; i++){
arrayBlockingQueue.offer(new FtpConnection());
}
}
/**
* 获取连接
* @return
*/
public FtpConnection getFtp(){
FtpConnection poll = null;
try {
poll = arrayBlockingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return poll;
}
/**
* 释放连接
* @param ftp
* @return
*/
public boolean relase(FtpConnection ftp){
return arrayBlockingQueue.offer(ftp);
}
/**
* 删除连接
* @param ftp
*/
public void remove(FtpConnection ftp){
arrayBlockingQueue.remove(ftp);
}
public void close(){
for(FtpConnection connection : arrayBlockingQueue){
try {
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.ourpalm.resupgrade.util.ftp;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.Callable;
import com.ourpalm.resupgrade.util.log.LoggerUtils;
/**
* 上传任务
* @author chuer
* @date 2015年1月7日 下午2:30:46
*/
public class UploadTask implements Callable{
private File file;
private FtpConnection ftp;
private String path;
private String fileName;
private FtpFactory factory;
public UploadTask(FtpFactory factory,FtpConnection ftp,File file,String path,String fileName){
this.factory = factory;
this.ftp = ftp;
this.file = file;
this.path = path;
this.fileName = fileName;
}
@Override
public UploadResult call() throws Exception {
UploadResult result = null;
try{
if(ftp == null){
result = new UploadResult(file.getAbsolutePath(),false);
return result;
}
//如果连接未开启 重新获取连接
if(!ftp.isConnected()){
factory.remove(ftp);
ftp = new FtpConnection();
// factory.relase(ftp);
}
//开始上传
LoggerUtils.upload.info(file.getName()+" is uploading ...");
FtpResult.resultList.add(file.getName()+" is uploading ...");
ftp.upload(path, fileName, file);
result = new UploadResult(file.getName(),true);
}catch(IOException ex){
result = new UploadResult(file.getName(),false);
ex.printStackTrace();
}finally{
factory.relase(ftp);//释放连接
}
FtpResult.resultList.add(result.toString());
LoggerUtils.upload.info(result.toString());
return result;
}
}
package com.ourpalm.resupgrade.util.ftp;
/**
* 上传结果
* @author chuer
* @date 2015年1月7日 下午2:31:14
*/
public class UploadResult {
private String fileName; //文件名称
private boolean result; //是否上传成功
public UploadResult(String fileName,boolean result){
this.fileName = fileName;
this.result = result;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public boolean isResult() {
return result;
}
public void setResult(boolean result) {
this.result = result;
}
public String toString(){
return "[fileName="+fileName+" , result="+result+"]";
}
}
package com.ourpalm.resupgrade.util.ftp;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 实时上传结果
* @author chuer
* @date 2015年1月7日 下午4:57:24
*/
public class FtpResult {
public static List resultList = new CopyOnWriteArrayList<>();
}
package com.ourpalm.resupgrade.util.ftp;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.ourpalm.resupgrade.config.FtpConfig;
/**
* ftp上传工具包
* @author chuer
* @date 2015年1月7日 下午2:31:39
*/
public class FtpUtil {
/**
* 上传文件
* @param ftpPath
* @param listFiles
* @return
*/
public static synchronized List upload(String ftpPath,File [] listFiles) {
ExecutorService newFixedThreadPool = Executors .newFixedThreadPool(FtpConfig.threadPoolSize);
List> results = new ArrayList<>();
FtpFactory factory = new FtpFactory();
for (File file : listFiles) {
FtpConnection ftp = factory.getFtp();
UploadTask upload = new UploadTask(factory,ftp, file, ftpPath, file.getName());
Future submit = newFixedThreadPool.submit(upload);
results.add(submit);
}
List listResults = new ArrayList<>();
for (Future result : results) {
try {
UploadResult uploadResult = result.get(30, TimeUnit.MINUTES);
listResults.add(uploadResult);
} catch (Exception e) {
e.printStackTrace();
}
}
factory.close();
newFixedThreadPool.shutdown();
return listResults;
}
}
package com.ourpalm.resupgrade.config;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.ourpalm.resupgrade.util.io.XmlUtils;
/**
* ftp 配置类
* @author chuer
* @date 2015年1月7日 下午4:36:50
*/
public class FtpConfig {
public static int defaultTimeoutSecond;
public static int connectTimeoutSecond;
public static int dataTimeoutSecond;
public static String host;
public static int port;
public static String user;
public static String password;
public static int threadPoolSize;
public static int ftpConnectionSize;
public static String rootPath;
/**
* @param path
*/
public static void load(String path) {
path += "ftpConfig.xml";
try{
Document doc = XmlUtils.load(path);
Element root = doc.getDocumentElement();
defaultTimeoutSecond = Integer.parseInt(XmlUtils.getChildText(root, "defaultTimeoutSecond"));
connectTimeoutSecond = Integer.parseInt(XmlUtils.getChildText(root, "connectTimeoutSecond"));
dataTimeoutSecond = Integer.parseInt(XmlUtils.getChildText(root, "dataTimeoutSecond"));
host = XmlUtils.getChildText(root, "host");
port = Integer.parseInt(XmlUtils.getChildText(root, "port"));
user = XmlUtils.getChildText(root, "user");
password = XmlUtils.getChildText(root, "password");
threadPoolSize = Integer.parseInt(XmlUtils.getChildText(root, "threadPoolSize"));
ftpConnectionSize = Integer.parseInt(XmlUtils.getChildText(root, "ftpConnectionSize"));
rootPath = XmlUtils.getChildText(root, "rootPath");
}catch(Exception e){
e.printStackTrace();
}
}
}
package com.ourpalm.resupgrade.util.ftp;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* 客户端
* @author chuer
* @date 2015年1月7日 下午2:32:41
*/
public class Client {
public static void main(String[] args) throws IOException {
String loalPath = "D:/resource/";
String ftpPath = "/resource";
File parentFile = new File(loalPath);
List resultLists = FtpUtil.upload(ftpPath,parentFile.listFiles());
for(UploadResult result : resultLists){
System.out.println(result);
}
}
}
雨林木风Ghost Win10 x64 专业版v2019.08 4768.37M | 2024-11-15
番茄花园Ghost Win10 64位 纯净版v2019.08 4099.14M | 2024-11-15
新萝卜家园Ghost Win10 64位 纯净版v2019.08 4099.14M | 2024-11-15
萝卜家园GHOST WIN10 64位 专业版 v2019.08 4204.78M | 2024-11-15
萝卜家园GHOST WIN10 86位 专业版 v2019.08 3098.34M | 2024-11-15
深度技术Ghost Win10 x86 纯净版v2019.08 3889.56M | 2024-11-15
Win8 32位 纯净企业版 2477.17M | 2024-11-15
Win8 32位 纯净专业版 2474.92M | 2024-11-15
GHOST win8.1 64位专业版v2018.11 3887.19M | 2024-11-15
深度技术Ghost win7 x86 特别旗舰版v2019.08 3682.63M | 2024-11-15
最新专题
类似植物大战僵尸游戏大全2024
免费唱歌软件大全2024
双人逃跑游戏有哪些2024
养鱼游戏大全2024