精华内容
下载资源
问答
  • AndFTP -- FTP登录软件 QuickEdit -- 代码编辑器

    AndFTP -- FTP登录软件

    QuickEdit -- 代码编辑器

    展开全文
  • Ubuntu上搭建ftp

    2015-09-30 18:21:19
    构建ftp服务器Date: 8.25 ...手机登录ftp 1.安装vsftpd系统缺省就有,直接安装 sudo apt-get install vsftpd 测试-下,输入 vsftpd -version 显示vsftpd: version 3.0.2。 在firefox上测试一下,输

    构建ftp服务器

    Date: 8.25
    Target: 构建ftp服务器

    参考

    1.安装vsftpd

    系统缺省就有,直接安装
    sudo apt-get install vsftpd
    测试-下,输入
    vsftpd -version
    显示vsftpd: version 3.0.2。
    在firefox上测试一下,输入ftp://localhostftp://192.168.0.104(内网ip),需要输入用户名和密码才能登录。成功后显示的是$HOME,也/home/wang(我现在的用户名)。能查看文件并下载,切换目录,但是不能修改。
    基本上ftpd都这步,用默认的就已经搭建完了。
    不过发现,这时vsftpd是running的,试着用手机登录却不行,暂时猜想可能是没用户吧。这个下文再提。

    2.修改vsftpd.conf

    这是实现需要的功能的地方,比如限制用户,限制下载什么的。
    修改的是/etc/vsftpd.conf。
    先保存原文件,恩,都是这样说的。
    cp /etc/vsftpd.conf /etc/vsftpd.conf.old
    不要自启动,那就要修改其启动脚本
    vim /etc/init/vsftpd.conf

    start on runlevel [2345] or net-device-up IFACE!=lo
    改为
    start on runlevel [345]
    这是改启动级别实现的,这里要根据运行级修改,一般默认是2。
    开始修改配置文件
    vim /etc/vsftpd.conf
    改变ftp根目录,GG跳转到文件尾,插入
    local_root=/home/wang/ftp
    其中ftp是我刚新建的文件夹,这时重新登录的话,一开始就不是/home/wang,而是local_root(刚设为/home/wang/ftp),说这是为了让用户不能随意操作其他目录。可是却能通过返回上一层到其他目录。后来发现firefox是这样,在chromium却是没这功能。
    我是个执着的人,所以钻牛角去了。又搜索了,在Wiki(?)上找到。在Shell上登录
    ftp localhost
    完了后再输入
    pwd
    不出意外发现是/home/wang/ftp,好,跟着干。
    chroot_local_user=YES
    试试×-×。再次登录,呵呵,显示
    500 OOPS: vsftpd: refusing to run with writable root inside chroot()。
    刚看过,忘了是什么鬼。幸亏度娘,比较和胃口的说法是用户的根目录可写,我们使用了chroot限制,在最近的更新里是不被允许的。要修复这个错误,可以
    chmod a-w /home/wang/ftp
    去除用户根目录的写权限。又或者简单点(我喜欢)。添加
    allow_wirteable_chroot=YES
    就ok了,登录,啪啪,行了.这样做后,用户也不能移动到其他目录了。
    用户登录时,我希望是带着口令的,即不能让匿名用户登录。那就是默认的效果,不用修改。不过,可以尝试一下匿名的登录
    anonymous_enable=NO
    发现/和先前带口令登录时不一样,看看就其实默认的是/srv/ftp。修改的话,可以先创建Tinuy文件夹
    mkdir /src/ftp/Tinuy
    mkdir /home/wang/Tinuy
    然后映射虚拟路径
    sudo mount --bind /srv/ftp/Tinuy /home/wang/Tinuy
    再登录时,的确是变了。改变其中一个文件夹,两个同时变。不过,怎么卸载我还没搞懂<-_<-,慎用慎用,哦米托佛。
    关于这里,其实还可以创建虚拟用户,不过这又是另话了。
    好了,碰壁完了,改回
    anoymous_enable=YES
    网上有课程说要修改/etc/init.d/vsftpd的,我在这里找不到这文件,可能是因为用的是Ubuntu 14,启动方式已经改为Upstart,为了支持原来的sysinit,服务的启动脚本是在/etc/init.d里面的,而缺省的运行级是2。在/etc/rc2.d里面没有其符号链接。
    如果是以前?/etc/init.d/vsftpd的确是有的。。。吧

    3.局域网登录

    在浏览器上输入ftp://localhost,或者ftp://127.0.0.1,或者ftp://自己的ip,就行了。

    4. 手机登录ftp

    然后又试着把vsftpd stop,用firefox再登录,发现这次不需要用户名和密码就能登上了,新建标签页也是。不过手机仍没登上。好吧,试着用chromium上,却登不上了。那firefox登上的是啥?。。。一段时间后,现在firefox也登不上了。
    再次start,这会登上。再次stop,chromium是立即断了连接,firefox是一段时间才登不上的。重新打开firefox也是登不上,需要start,加上用户名和密码才行。
    难不成stop不是真正的关?stop时显示vsftpd stop/waiting,waiting是why?这让我想起来那背得欲仙欲死的操作系统,是那进程状态阻塞态或挂起态?
    好吧,暂且不管。总之已经安装了vsftpd。service vsftpd start/stop是启。动和关闭。
    本地登录行,用手机却登不上,局域网内的啊?!难道是防火墙?试试
    ufw enable
    允许ftp端口(默认是21)

    ufw default allow
    ufw allow ftp
    ufw status

    开了以后就只能使用ftp登录了,理论上是,但手机登录的时候确实没反应。ping一下,能通。后来开了apache2,也是登不上http。关了ufw
    ufw disable
    http能登上了,说明防火墙是有用的。关了后也是上不了ftp,而开机是ufw默认是关了,综上就不是防火墙的错,路走歪了。难道是端口问题?在vsftpd.conf里添加了
    listen_port=2121
    试一下。还是本地可行,与端口无关。先还原端口
    netstat -nl
    显示21在监听,不过手机还是不行不行的。苦恼中。。。

    Date 8.27
    想到通过日志文件去看。先重启vsftpd
    sudo service vsftpd restart
    然后用电脑和手机登录。
    然后直接去查看日志文件
    cat /var/log/vsftpd.log | grep "Aug 27"
    显示如下

    Thu Aug 27 09:55:16 2015 [pid 4392] CONNECT: Client "127.0.0.1"
    Thu Aug 27 09:55:18 2015 [pid 4391] [anonymous] FAIL LOGIN: Client "127.0.0.1"
    Thu Aug 27 09:55:19 2015 [pid 4395] CONNECT: Client "127.0.0.1"
    Thu Aug 27 09:55:21 2015 [pid 4394] [wang] OK LOGIN: Client "127.0.0.1"
    Thu Aug 27 09:55:28 2015 [pid 4399] [wang] OK DOWNLOAD: Client "127.0.0.1", "/chromium-browser.desktop", 12615 bytes, 194.36Kbyte/sec
    Thu Aug 27 09:56:15 2015 [pid 4412] CONNECT: Client "192.168.0.104"
    Thu Aug 27 09:56:17 2015 [pid 4411] [anonymous] FAIL LOGIN: Client "192.168.0.104"
    Thu Aug 27 09:56:18 2015 [pid 4416] CONNECT: Client "192.168.0.104"
    Thu Aug 27 09:56:19 2015 [pid 4414] [wang] OK LOGIN: Client "192.168.0.104"
    Thu Aug 27 09:56:21 2015 [pid 4419] [wang] OK DOWNLOAD: Client "192.168.0.104", "/chromium-browser.desktop", 12615 bytes, 42924.52Kbyte/sec

    可以看出匿名用户登不上,只能实名制(原来打开文件也是要下载的)。而手机CONNECT显示都没有。应该是手机问题或者是vsftpd配置文件的问题,研究研究。

    Date 9.4
    后来发现,原来是手机里面的浏览器不支持ftp,下载es或者支持ftp的浏览器就行了。一直以为浏览器支持http和ftp,其实不然,浏览器本身支持http,ftp只是添加的功能,IE、firefox都是支持ftp的,让我误以为浏览器肯定支持ftp的。问题成功解决。

    展开全文
  • FTP是基于FTP协议来实现...现在有了SwiFTP的开源库,只要对其稍加改造,就可以将手机快速变成一个FTP的服务器。这里提供一个SwiFTP的下载地址https://github.com/sparkleDai/swiftp。 我们先来看看SwiFTP源码中实现...

    FTP是基于FTP协议来实现文件的管理,理论上只要将协议逐个实现,就可以实现一个FTP的服务端了,但需要一些时间,而且还是个体力活。现在有了SwiFTP的开源库,只要对其稍加改造,就可以将手机快速变成一个FTP的服务器。这里提供一个SwiFTP的下载地址https://github.com/sparkleDai/swiftp

    我们先来看看SwiFTP源码中实现的效果图:



    前一个图是FTP的配置,后一个图是FTP服务器控制。这两个画面,一看就是一嘛黑的,老外貌似比较喜欢这种风格。下面是修改后的效果图。



    修改后的FTP服务端只有一个启动/停止的按钮,其他的都采用默认设置。下面我们来看具体的修改步骤。

    1、跳过配置画面

    SwiFTP一开启就会跳到配置画面,经查代码,发现是在ServerControlActivity的OnResume中跳转过来的,所以只要想办法跳过去就可以了。要跳过去,有几种方式,最直接的就是屏蔽掉。不过,考虑到原来有配置画面,这里可能需要留一个口来增加设置,所以这里我增加了一个配置函数,将所需要的配置项配置成了默认项,代码如下。(我将ServerControlActivity改成了MainActivity)

    MainActivity.java
    	protected void onResume() {
    		super.onResume();
    		// If the required preferences are not present, launch the configuration
    		// Activity.
    		configSetting();
    		if (!requiredSettingsDefined()) {
    			launchCONFIG_KEYS();
    		}
    		UiUpdater.registerClient(handler);
    		updateUi();
    		// Register to receive wifi status broadcasts
    		myLog.l(Log.DEBUG, "Registered for wifi updates");
    		this.registerReceiver(wifiReceiver, new IntentFilter(
    				WifiManager.WIFI_STATE_CHANGED_ACTION));
    	}
    
    	private void configSetting() {
    		// Validation was successful, save the settings object
    		SharedPreferences settings = getSharedPreferences(
    				Defaults.getSettingsName(), Defaults.getSettingsMode());
    		SharedPreferences.Editor editor = settings.edit();
    
    		editor.putString(CONFIG_KEYS.USERNAME, Defaults.username);
    		editor.putString(CONFIG_KEYS.PASSWORD, Defaults.password);
    		editor.putInt(CONFIG_KEYS.PORTNUM, 2121);
    		editor.putString(CONFIG_KEYS.CHROOTDIR, Defaults.chrootDir);
    		editor.putBoolean(CONFIG_KEYS.ACCEPT_WIFI, Defaults.acceptWifi);
    		editor.putBoolean(CONFIG_KEYS.ACCEPT_NET, Defaults.acceptNet);
    		editor.putBoolean(CONFIG_KEYS.STAY_AWAKE, Defaults.stayAwake);
    		editor.putBoolean(CONFIG_KEYS.IS_ANONYMOUS, Defaults.isAnonymous);
    		editor.commit();
    		
    	}
    

    CONFIG_KEYS.java
    package com.sparkle.ftp;
    
    public class CONFIG_KEYS {
    	public final static String USERNAME = "username";
    	public final static String PASSWORD = "password";
    	public final static String PORTNUM = "portNum";
    	public final static String CHROOTDIR = "chrootDir";
    	public final static String ACCEPT_WIFI = "allowWifi";
    	public final static String ACCEPT_NET = "allowNet";
    	public final static String STAY_AWAKE = "stayAwake";
    	public final static String IS_ANONYMOUS="isAnonymous";
    }
    

    Defaults.java部分代码
    	protected static int inputBufferSize = 256;
    	protected static int dataChunkSize = 65536;  // do file I/O in 64k chunks 
    	protected static int sessionMonitorScrollBack = 10;
    	protected static int serverLogScrollBack = 10;
    	protected static int uiLogLevel = Defaults.release ? Log.INFO : Log.DEBUG;
    	protected static int consoleLogLevel = Defaults.release ? Log.INFO : Log.DEBUG;
    	protected static String settingsName = "FTP";
    	public static String username = "Anonynous";
    	public static String password = "";
    	protected static int portNumber = 2121; 
    //	protected static int ipRetrievalAttempts = 5;
    	public static final int tcpConnectionBacklog = 5;
    	public static final String chrootDir = Environment.getExternalStorageDirectory().getAbsolutePath();
    	public static final boolean acceptWifi = true;
    	public static final boolean acceptNet = false; // don't incur bandwidth charges
    	public static final boolean stayAwake = false;
    	public static final boolean isAnonymous=true;
    	public static final int REMOTE_PROXY_PORT = 2222;
    	public static final String STRING_ENCODING = "UTF-8";
    	public static final int SO_TIMEOUT_MS = 30000; // socket timeout millis


    注:
    (1)、Activity的生命周期是OnCreate->OnStart->OnResume->OnPause->OnStop->OnDestory。在activity1跳转到另一个activity2后,如果跳转时activity1没有finish掉,那么activity2关闭跳后,activity1会从activity堆栈中重新唤醒,也就是会调用OnResume。所以在配置的activity中,如果没有配置,当点cancel,配置的activity虽然被关闭了,但是当回到服务控制的activity后,又激活了OnResume,然后判断配置的情况,如果不符合,又会启动配置的activity。所以会发现,如果没有配置,即使点cancel也没有作用,不知道的还以为中镖了。
    (2)、SharedPreference是共享数据的一种方式,可以实现跨activity的数据共享,是一个轻量的存储方式,本质上是一个xml的key-value对。对其修改数据时,需要请求edit,然后修改数据,最后还要commit。这个和提交代码到git/svn等类似。
    (3)、CONFIG_KEYS是将原来配置的activity中的一些key写到了这个类中。
    (4)、Defaults中增加了username、password、isAnonymous。其中isAnonymous是为了实现FTP的匿名访问而增设的一个配置项。
    (5)、由于手机内存中的文件不一定都能访问,所以FTP默认的目录设置到了SD卡中。在不同的设备中,对于SD卡的路径有所不同,所以采用了系统自带的函数来实现,即chrootDir = Environment.getExternalStorageDirectory().getAbsolutePath()。

    2、实现匿名访问
    现在将服务端跑起来后,已经可以正常访问了,不过每次访问的时候都会弹出用户名和密码的输入框,让人很烦,所以就想着法子的屏蔽掉。既然会弹出这个框,程序中肯定会有对应的判断项,经过查找,发现是在FTP中的PASS命令中实现的。所以对CmdPASS.java作了些修改,修改部分的代码如下。
    public void run() {
    		// User must have already executed a USER command to
    		// populate the Account object's username
    		myLog.l(Log.DEBUG, "Executing PASS");
    		Context ctx = Globals.getContext();
    		SharedPreferences settings = ctx.getSharedPreferences(
    				Defaults.getSettingsName(), Defaults.getSettingsMode());
    		boolean isAnonymous=settings.getBoolean(CONFIG_KEYS.IS_ANONYMOUS,false);
    		if(isAnonymous)
    		{
    			sessionThread.writeString("230 Access granted\r\n");
    			myLog.l(Log.INFO, "Anonymous visit!");
    			sessionThread.authAttempt(true);
    			return;
    		}
    		
    		if(ctx == null) {
    			// This will probably never happen, since the global 
    			// context is configured by the Service
    			myLog.l(Log.ERROR, "No global context in PASS\r\n");
    		}
    		
    		String attemptPassword = getParameter(input, true); // silent
    		String attemptUsername = sessionThread.account.getUsername();
    		if(attemptUsername == null) {
    			sessionThread.writeString("503 Must send USER first\r\n");
    			return;
    		}
    	
    		String username = settings.getString("username", null);
    		String password = settings.getString("password", null);
    		if(username == null || password == null) {
    			myLog.l(Log.ERROR, "Username or password misconfigured");
    			sessionThread.writeString("500 Internal error during authentication");
    		} else if(username.equals(attemptUsername) && 
    				password.equals(attemptPassword)) {
    			sessionThread.writeString("230 Access granted\r\n");
    			myLog.l(Log.INFO, "User " + username + " password verified");
    			sessionThread.authAttempt(true);
    		} else {
    			try {
    				// If the login failed, sleep for one second to foil
    				// brute force attacks
    				Thread.sleep(1000);
    			} catch(InterruptedException e) {}
    			myLog.l(Log.INFO, "Failed authentication");
    			sessionThread.writeString("530 Login incorrect.\r\n");
    			sessionThread.authAttempt(false);
    		}
    	}

    注:
    (1)、通过SharedPreferences获取配置信息,判断是否配置为匿名访问,如果是,则不再校验用户名和密码。
    (2)、sessionThread.writeString("230 Access granted\r\n")和sessionThread.authAttempt(true)是授权是否允许的部分,true是允许,false是不允许。

    3、界面的实现
    对于界面,重新写了一个,下面是界面的代码。
    MainActivity.java
    package com.sparkle.ftp;
    
    import java.net.InetAddress;
    
    import android.annotation.SuppressLint;
    import android.app.Activity;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.content.SharedPreferences;
    import android.net.wifi.WifiManager;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.view.Gravity;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.Window;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.TextView;
    import android.widget.Toast;
    
    public class MainActivity extends Activity {
    
    	private Button _startStop_Button=null;
    	private TextView _ip_TextView=null;
    	private ImageView _ftpStatus_imageView=null;
    
    	protected MyLog myLog = new MyLog(this.getClass().getName());
    
    	protected Context activityContext = this;
    
    	@SuppressLint("HandlerLeak")
    	public Handler handler = new Handler() {
    		public void handleMessage(Message msg) {
    			switch (msg.what) {
    			case 0: // We are being told to do a UI update
    				// If more than one UI update is queued up, we only need to do
    				// one.
    				removeMessages(0);
    				updateUi();
    				break;
    			case 1: // We are being told to display an error message
    				removeMessages(1);
    			}
    		}
    	};
    
    	public MainActivity() {
    
    	}
    
    	/** Called with the activity is first created. */
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    
    		// Request no title bar on our window
    		//requestWindowFeature(Window.FEATURE_NO_TITLE);
    
    		// Set the application-wide context global, if not already set
    		Context myContext = Globals.getContext();
    		if (myContext == null) {
    			myContext = getApplicationContext();
    			if (myContext == null) {
    				throw new NullPointerException("Null context!?!?!?");
    			}
    			Globals.setContext(myContext);
    		}
    		// Inflate our UI from its XML layout description.
    		setContentView(R.layout.main_activity);
    
    		_ip_TextView = (TextView) findViewById(R.id.ip_address);
    		_ftpStatus_imageView=(ImageView)findViewById(R.id.ftp_status);
    		_startStop_Button = (Button) findViewById(R.id.start_stop_button);
    
    		_startStop_Button.setOnClickListener(startStopListener);
    
    	}
    
    	/**
    	 * Whenever we regain focus, we should update the button text depending on
    	 * the state of the server service.
    	 */
    	protected void onStart() {
    		super.onStart();
    		UiUpdater.registerClient(handler);
    		updateUi();
    	}
    
    	protected void onResume() {
    		super.onResume();
    		// If the required preferences are not present, launch the configuration
    		// Activity.
    		configSetting();
    		if (!requiredSettingsDefined()) {
    			launchCONFIG_KEYS();
    		}
    		UiUpdater.registerClient(handler);
    		updateUi();
    		// Register to receive wifi status broadcasts
    		myLog.l(Log.DEBUG, "Registered for wifi updates");
    		this.registerReceiver(wifiReceiver, new IntentFilter(
    				WifiManager.WIFI_STATE_CHANGED_ACTION));
    	}
    
    	/*
    	 * Whenever we lose focus, we must unregister from UI update messages from
    	 * the FTPServerService, because we may be deallocated.
    	 */
    	protected void onPause() {
    		super.onPause();
    		UiUpdater.unregisterClient(handler);
    		myLog.l(Log.DEBUG, "Unregistered for wifi updates");
    		this.unregisterReceiver(wifiReceiver);
    	}
    
    	protected void onStop() {
    		super.onStop();
    		UiUpdater.unregisterClient(handler);
    	}
    
    	protected void onDestroy() {
    		super.onDestroy();
    		UiUpdater.unregisterClient(handler);
    	}
    
    	private void configSetting() {
    		// Validation was successful, save the settings object
    		SharedPreferences settings = getSharedPreferences(
    				Defaults.getSettingsName(), Defaults.getSettingsMode());
    		SharedPreferences.Editor editor = settings.edit();
    
    		editor.putString(CONFIG_KEYS.USERNAME, Defaults.username);
    		editor.putString(CONFIG_KEYS.PASSWORD, Defaults.password);
    		editor.putInt(CONFIG_KEYS.PORTNUM, 2121);
    		editor.putString(CONFIG_KEYS.CHROOTDIR, Defaults.chrootDir);
    		editor.putBoolean(CONFIG_KEYS.ACCEPT_WIFI, Defaults.acceptWifi);
    		editor.putBoolean(CONFIG_KEYS.ACCEPT_NET, Defaults.acceptNet);
    		editor.putBoolean(CONFIG_KEYS.STAY_AWAKE, Defaults.stayAwake);
    		editor.putBoolean(CONFIG_KEYS.IS_ANONYMOUS, Defaults.isAnonymous);
    		editor.commit();
    		
    	}
    
    	/**
    	 * This will be called by the static UiUpdater whenever the service has
    	 * changed state in a way that requires us to update our UI.
    	 * 
    	 * We can't use any myLog.l() calls in this function, because that will
    	 * trigger an endless loop of UI updates.
    	 */
    	public void updateUi() {
    		WifiManager wifiMgr = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    		int wifiState = wifiMgr.getWifiState();
    		myLog.l(Log.DEBUG, "Updating UI", true);
    		if (FTPServerService.isRunning()) {
    			myLog.l(Log.DEBUG, "updateUi: server is running", true);
    			// Put correct text in start/stop button
    			_startStop_Button.setText(R.string.stop_server);
    
    			// Fill in wifi status and address
    			InetAddress address = FTPServerService.getWifiIp();
    			if (address != null) {
    				_ip_TextView.setText("ftp://" + address.getHostAddress() + ":"
    						+ FTPServerService.getPort() + "/");
    				_ftpStatus_imageView.setBackgroundDrawable(getResources().getDrawable(R.drawable.ftp_on));
    			} else {
    				myLog.l(Log.VERBOSE, "Null address from getServerAddress()",
    						true);
    				_ip_TextView.setText(R.string.cant_get_url);
    				_ftpStatus_imageView.setBackgroundDrawable(getResources().getDrawable(R.drawable.ftp_off));
    			}
    		} else {
    			myLog.l(Log.DEBUG, "updateUi: server is not running", true);
    			// Update the start/stop button to show the correct text
    			_startStop_Button.setText(R.string.start_server);
    			_ip_TextView.setText(R.string.no_url_yet);
    			_startStop_Button.setText(R.string.start_server);
    			_ftpStatus_imageView.setBackgroundDrawable(getResources().getDrawable(R.drawable.ftp_off));
    		}
    	}
    
    	/**
    	 * Called when your activity's options menu needs to be created.
    	 */
    	@Override
    	public boolean onCreateOptionsMenu(Menu menu) {
    		super.onCreateOptionsMenu(menu);
    		return true;
    	}
    
    	/**
    	 * Called right before your activity's option menu is displayed.
    	 */
    	@Override
    	public boolean onPrepareOptionsMenu(Menu menu) {
    		super.onPrepareOptionsMenu(menu);
    		return true;
    	}
    
    	/**
    	 * Called when a menu item is selected.
    	 */
    	@Override
    	public boolean onOptionsItemSelected(MenuItem item) {
    		/*
    		 * switch (item.getItemId()) { case BACK_ID: finish(); return true; case
    		 * CLEAR_ID: mEditor.setText(""); return true; }
    		 */
    
    		return super.onOptionsItemSelected(item);
    	}
    
    	OnClickListener startStopListener = new OnClickListener() {
    		public void onClick(View v) {
    			Context context = getApplicationContext();
    			Intent intent = new Intent(context, FTPServerService.class);
    			/*
    			 * In order to choose whether to stop or start the server, we check
    			 * the text on the button to see which action the user was
    			 * expecting.
    			 */
    			String startString = getString(R.string.start_server);
    			String stopString = getString(R.string.stop_server);
    			String buttonText = _startStop_Button.getText().toString();
    			if (buttonText.equals(startString)) {
    				/* The button had the "start server" text */
    				if (!FTPServerService.isRunning()) {
    					warnIfNoExternalStorage();
    					context.startService(intent);
    				}
    			} else if (buttonText.equals(stopString)) {
    				/*
    				 * The button had the "stop server" text. We stop the server
    				 * now.
    				 */
    				context.stopService(intent);
    			} else {
    				// Do nothing
    				myLog.l(Log.ERROR, "Unrecognized start/stop text");
    			}
    		}
    	};
    
    	private void warnIfNoExternalStorage() {
    		String storageState = Environment.getExternalStorageState();
    		if (!storageState.equals(Environment.MEDIA_MOUNTED)) {
    			myLog.i("Warning due to storage state " + storageState);
    			Toast toast = Toast.makeText(this, R.string.storage_warning,
    					Toast.LENGTH_LONG);
    			toast.setGravity(Gravity.CENTER, 0, 0);
    			toast.show();
    		}
    	}
    
    	OnClickListener addUserListener = new OnClickListener() {
    		public void onClick(View v) {
    			myLog.l(Log.INFO, "Add user stub");
    		}
    	};
    
    	OnClickListener manageUsersListener = new OnClickListener() {
    		public void onClick(View v) {
    			myLog.l(Log.INFO, "Manage users stub");
    		}
    	};
    
    	OnClickListener serverOptionsListener = new OnClickListener() {
    		public void onClick(View v) {
    			myLog.l(Log.INFO, "Server options stub");
    		}
    	};
    
    	DialogInterface.OnClickListener ignoreDialogListener = new DialogInterface.OnClickListener() {
    		public void onClick(DialogInterface dialog, int which) {
    		}
    	};
    
    	/**
    	 * A call-back for when the user presses the "setup" button.
    	 */
    	OnClickListener setupListener = new OnClickListener() {
    		public void onClick(View v) {
    			launchCONFIG_KEYS();
    		}
    	};
    
    	void launchCONFIG_KEYS() {
    		if (!requiredSettingsDefined()) {
    			Toast toast = Toast.makeText(this, R.string.must_config,
    					Toast.LENGTH_SHORT);
    			toast.setGravity(Gravity.CENTER, 0, 0);
    			toast.show();
    		}
    		Intent intent = new Intent(activityContext, CONFIG_KEYS.class);
    		startActivity(intent);
    	}
    
    	/**
    	 * A callback for when the user toggles the session monitor on or off
    	 */
    	OnClickListener sessionMonitorCheckBoxListener = new OnClickListener() {
    		public void onClick(View v) {
    			// Trigger a UI update message to our Activity
    			UiUpdater.updateClients();
    			// updateUi();
    		}
    	};
    
    	/**
    	 * A callback for when the user toggles the server log on or off
    	 */
    	OnClickListener serverLogCheckBoxListener = new OnClickListener() {
    		public void onClick(View v) {
    			// Trigger a UI update message to our Activity
    			UiUpdater.updateClients();
    			// updateUi();
    		}
    	};
    
    	BroadcastReceiver wifiReceiver = new BroadcastReceiver() {
    		public void onReceive(Context ctx, Intent intent) {
    			myLog.l(Log.DEBUG, "Wifi status broadcast received");
    			updateUi();
    		}
    	};
    
    	boolean requiredSettingsDefined() {
    		SharedPreferences settings = getSharedPreferences(
    				Defaults.getSettingsName(), Defaults.getSettingsMode());
    		String username = settings.getString("username", null);
    		String password = settings.getString("password", null);
    		if (username == null || password == null) {
    			return false;
    		} else {
    			return true;
    		}
    	}
    
    	/**
    	 * Get the settings from the FTPServerService if it's running, otherwise
    	 * load the settings directly from persistent storage.
    	 */
    	SharedPreferences getSettings() {
    		SharedPreferences settings = FTPServerService.getSettings();
    		if (settings != null) {
    			return settings;
    		} else {
    			return this.getPreferences(MODE_PRIVATE);
    		}
    	}
    
    }
    



    main_activity.xml
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="vertical"
        tools:context=".MainActivity" >
    
        <LinearLayout
            android:id="@+id/content"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            android:gravity="center"
            android:orientation="vertical" >
    
            <ImageView
                android:id="@+id/ftp_status"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/ftp_off" />
    
            <TextView
                android:id="@+id/ip_address"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/no_url_yet"
                android:textColor="#000000"
                android:textSize="25sp" />
        </LinearLayout>
    
        <LinearLayout
            android:id="@+id/bottom_panel"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="30dp"
            android:gravity="center"
            android:orientation="horizontal" >
    
            <Button
                android:id="@+id/start_stop_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/start_server"
                android:background="@drawable/button_bg"
                android:textSize="40sp" />
        </LinearLayout>
    
    </RelativeLayout>

    styles.xml
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <style name="ActionButton">
            <item name="android:layout_width">wrap_content</item>
            <item name="android:layout_height">wrap_content</item>
            <item name="android:textAppearance">@style/TextAppearance.ActionButton</item>
        </style>
    
        <style name="TextAppearance" parent="android:TextAppearance"></style>
    
        <style name="AppBaseTheme" parent="android:Theme.Light"></style>
    
        <style name="TextAppearance.ActionButton">
            <item name="android:textStyle">italic</item>
        </style>
    
        <style name="AppTheme" parent="AppBaseTheme">
            <item name="android:windowTitleBackgroundStyle">@style/CustomWindowTitleBackground</item>
            <item name="android:windowTitleStyle">@style/CustomWindowTitle</item>
        </style>
    
        <style name="CustomWindowTitleBackground">
            <item name="android:background">@drawable/title_bg_blue</item>
        </style>
    
        <style name="CustomWindowTitle" parent="AppBaseTheme">
            <item name="android:textAppearance">@style/CustomWindowTitleText</item>
            <item name="android:layout_gravity">center</item>
        </style>
    
        <style name="CustomWindowTitleText" parent="android:TextAppearance.WindowTitle">
            <item name="android:textColor">#ffffff</item>
            <item name="android:textSize">14sp</item>
            <item name="android:textStyle">bold</item>
        </style>
    
    </resources>

    strings.xml
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <string name="name_version">电脑管理手机文件 1.1.1</string>
        <string name="app_name">电脑管理手机文件</string>
        <string name="start_server"><b>启动</b></string>
        <string name="stop_server"><b>停止</b></string>
        <string name="no_url_yet"></string>
        <string name="my_url_is">电脑上输入:</string>
        <string name="cant_get_url">无法获取IP</string>
        <string name="storage_warning">没有SD卡</string>
        <string name="must_config">FTP未配置</string>
        
    </resources>

    FTPServerService.java
    package com.sparkle.ftp;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.InetAddress;
    import java.net.InetSocketAddress;
    import java.net.ServerSocket;
    import java.util.ArrayList;
    import java.util.List;
    
    import android.app.Notification;
    import android.app.NotificationManager;
    import android.app.PendingIntent;
    import android.app.Service;
    import android.content.Context;
    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.net.wifi.WifiManager;
    import android.net.wifi.WifiManager.WifiLock;
    import android.os.IBinder;
    import android.os.PowerManager;
    import android.util.Log;
    
    
    public class FTPServerService extends Service implements Runnable {
    	protected static Thread serverThread = null;
    	protected boolean shouldExit = false;
    	protected MyLog myLog = new MyLog(getClass().getName());
    	protected static MyLog staticLog = 
    		new MyLog(FTPServerService.class.getName());
    	
    	public static final int BACKLOG = 21;
    	public static final int MAX_SESSIONS = 5;
    	public static final String WAKE_LOCK_TAG = "SwiFTP";
    	
    	//protected ServerSocketChannel wifiSocket;
    	protected ServerSocket listenSocket;
    	protected static WifiLock wifiLock = null;
    	
    //	protected static InetAddress serverAddress = null;
    	
    	protected static List<String> sessionMonitor = new ArrayList<String>();
    	protected static List<String> serverLog = new ArrayList<String>();
    	protected static int uiLogLevel = Defaults.getUiLogLevel();
    	
    	// The server thread will check this often to look for incoming 
    	// connections. We are forced to use non-blocking accept() and polling
    	// because we cannot wait forever in accept() if we want to be able
    	// to receive an exit signal and cleanly exit.
    	public static final int WAKE_INTERVAL_MS = 1000; // milliseconds
    	
    	protected static int port;
    	protected static boolean acceptWifi;
    	protected static boolean acceptNet;
    	protected static boolean fullWake;
    	
    	private TcpListener wifiListener = null;
    	private List<SessionThread> sessionThreads = new ArrayList<SessionThread>();
    	
    	private static SharedPreferences settings = null;
    	
    	NotificationManager notificationMgr = null;
    	PowerManager.WakeLock wakeLock; 	
    	
    	public FTPServerService() {
    	}
    
    	public IBinder onBind(Intent intent) {
    		// We don't implement this functionality, so ignore it
    		return null;
    	}
    	
    	@Override
    	public void onCreate() {
    		myLog.l(Log.DEBUG, "SwiFTP server created");
    		// Set the application-wide context global, if not already set
    		Context myContext = Globals.getContext();
    		if(myContext == null) {
    			myContext = getApplicationContext();
    			if(myContext != null) {
    				Globals.setContext(myContext);
    			}
    		}
    		return;
    	}
    	
    	public void onStart(Intent intent, int startId ){
    		super.onStart(intent, startId);
    		
    		shouldExit = false;
    		int attempts = 10;
    		// The previous server thread may still be cleaning up, wait for it
    		// to finish.
    		while(serverThread != null) {
    			myLog.l(Log.WARN, "Won't start, server thread exists");
    			if(attempts > 0) {
    				attempts--;
    				Util.sleepIgnoreInterupt(1000);
    			} else {
    				myLog.l(Log.ERROR, "Server thread already exists");
    				return;
    			}
    		}
    		myLog.l(Log.DEBUG, "Creating server thread");
    		serverThread = new Thread(this);
    		serverThread.start();
    		
    		// todo: we should broadcast an intent to inform anyone who cares
    	}
    	
    	public static boolean isRunning() {
    		// return true if and only if a server Thread is running
    		if(serverThread == null) {
    			staticLog.l(Log.DEBUG, "Server is not running (null serverThread)");
    			return false;
    		}
    		if(!serverThread.isAlive()) {
    			staticLog.l(Log.DEBUG, "serverThread non-null but !isAlive()");
    		} else {
    			staticLog.l(Log.DEBUG, "Server is alive");
    		}
    		return true;
    	}
    	
    	public void onDestroy() {
    		myLog.l(Log.INFO, "onDestroy() Stopping server");
    		shouldExit = true;
    		if(serverThread == null) {
    			myLog.l(Log.WARN, "Stopping with null serverThread");
    			return;
    		} else {
    			serverThread.interrupt();
    			try {
    				serverThread.join(10000);  // wait 10 sec for server thread to finish
    			} catch (InterruptedException e) {}
    			if(serverThread.isAlive()) {
    				myLog.l(Log.WARN, "Server thread failed to exit");
    				// it may still exit eventually if we just leave the
    				// shouldExit flag set
    			} else {
    				myLog.d("serverThread join()ed ok");
    				serverThread = null;
    			}
    		}
    		try {
    			if(listenSocket != null) {
    				myLog.l(Log.INFO, "Closing listenSocket");
    				listenSocket.close();
    			}
    		} catch (IOException e) {}
    
    		UiUpdater.updateClients();
    		if(wifiLock != null) {
    			wifiLock.release();
    			wifiLock = null;
    		}
    		clearNotification();
    		myLog.d("FTPServerService.onDestroy() finished");
    	}
    	
    	private boolean loadSettings() {
    		myLog.l(Log.DEBUG, "Loading settings");
    		settings = getSharedPreferences(
    				Defaults.getSettingsName(), Defaults.getSettingsMode());
    		port = settings.getInt("portNum", Defaults.portNumber);
    		if(port == 0) {
    			// If port number from settings is invalid, use the default
    			port = Defaults.portNumber;
    		}
    		myLog.l(Log.DEBUG, "Using port " + port);
    		
    		acceptNet = settings.getBoolean(CONFIG_KEYS.ACCEPT_NET,
    									    Defaults.acceptNet);
    		acceptWifi = settings.getBoolean(CONFIG_KEYS.ACCEPT_WIFI,
    										 Defaults.acceptWifi);
    		fullWake = settings.getBoolean(CONFIG_KEYS.STAY_AWAKE,
    										 Defaults.stayAwake);
    		
    		// The username, password, and chrootDir are just checked for sanity	
    		
    		String username = settings.getString(CONFIG_KEYS.USERNAME, Defaults.username);
    		String password = settings.getString(CONFIG_KEYS.PASSWORD, Defaults.password);
    		String chrootDir = settings.getString(CONFIG_KEYS.CHROOTDIR,
    				Defaults.chrootDir);
    		
    		validateBlock: {
    			if(username == null || password == null) {
    				myLog.l(Log.ERROR, "Username or password is invalid");
    				break validateBlock;
    			}
    			File chrootDirAsFile = new File(chrootDir);
    			if(!chrootDirAsFile.isDirectory()) {
    				myLog.l(Log.ERROR, "Chroot dir is invalid");
    				break validateBlock;
    			}
    			Globals.setChrootDir(chrootDirAsFile);
    			Globals.setUsername(username);
    			return true;
    		}
    		// We reach here if the settings were not sane
    		return false;
    	}
    	
    	// This opens a listening socket on all interfaces. 
    	void setupListener() throws IOException {
    		listenSocket = new ServerSocket();
    		listenSocket.setReuseAddress(true);
    		listenSocket.bind(new InetSocketAddress(port));
    	}
    	
    	private void setupNotification() {
    		// http://developer.android.com/guide/topics/ui/notifiers/notifications.html
    		
    		// Get NotificationManager reference
    		String ns = Context.NOTIFICATION_SERVICE;
    		notificationMgr = (NotificationManager) getSystemService(ns);
    		
    		// Instantiate a Notification
    		int icon = R.drawable.notification;
    
    		long when = System.currentTimeMillis();
    	
    	}
    	
    	private void clearNotification() {
    		if(notificationMgr == null) {
    			// Get NotificationManager reference
    			String ns = Context.NOTIFICATION_SERVICE;
    			notificationMgr = (NotificationManager) getSystemService(ns);
    		}
    		notificationMgr.cancelAll();
    		myLog.d("Cleared notification");
    	}
    	
    	public void run() {
    		// The UI will want to check the server status to update its 
    		// start/stop server button
    		int consecutiveProxyStartFailures = 0;
    		long proxyStartMillis = 0;
    
    		UiUpdater.updateClients();
    				
    		myLog.l(Log.DEBUG, "Server thread running");
    		
    		// set our members according to user preferences
    		if(!loadSettings()) {
    			// loadSettings returns false if settings are not sane
    			cleanupAndStopService();
    			return;
    		}
    		
    		
    		// Initialization of wifi
    		if(acceptWifi) {
    			// If configured to accept connections via wifi, then set up the socket
    			try {
    				setupListener();
    			} catch (IOException e) {
    				myLog.l(Log.WARN, "Error opening port, check your network connection.");
    //				serverAddress = null;
    				cleanupAndStopService();
    				return;
    			}
    			takeWifiLock();
    		}		
    		takeWakeLock();
    		
    		myLog.l(Log.INFO, "SwiFTP server ready");
    		setupNotification();
    
    		// We should update the UI now that we have a socket open, so the UI
    		// can present the URL
    		UiUpdater.updateClients();
    		
    		while(!shouldExit) {
    			if(acceptWifi) {
    				if(wifiListener != null) {
    					if(!wifiListener.isAlive()) {
    						myLog.l(Log.DEBUG, "Joining crashed wifiListener thread");
    						try {
    							wifiListener.join();
    						} catch (InterruptedException e) {}
    						wifiListener = null;
    					}
    				}
    				if(wifiListener == null) {
    					// Either our wifi listener hasn't been created yet, or has crashed,
    					// so spawn it
    					wifiListener = new TcpListener(listenSocket, this); 
    					wifiListener.start();
    				}
    			}
    			
    			try {
    				// todo: think about using ServerSocket, and just closing
    				// the main socket to send an exit signal
    				Thread.sleep(WAKE_INTERVAL_MS);
    			} catch(InterruptedException e) {
    				myLog.l(Log.DEBUG, "Thread interrupted");
    			}
    		}
    			
    		terminateAllSessions();
    
    	
    		if(wifiListener != null) {
    			wifiListener.quit();
    			wifiListener = null;
    		}
    		shouldExit = false; // we handled the exit flag, so reset it to acknowledge
    		myLog.l(Log.DEBUG, "Exiting cleanly, returning from run()");
    		clearNotification();
    		releaseWakeLock();
    		releaseWifiLock();		
    	}
    	
    	private void terminateAllSessions() {
    		myLog.i("Terminating " + sessionThreads.size() + " session thread(s)");
    		synchronized(this) {
    			for(SessionThread sessionThread : sessionThreads) {
    				if(sessionThread != null) {
    					sessionThread.closeDataSocket();
    					sessionThread.closeSocket();
    				}
    			}
    		}
    	}
    	
    	public void cleanupAndStopService() {
    		// Call the Android Service shutdown function
    		Context context = getApplicationContext();
    		Intent intent = new Intent(context,	FTPServerService.class);
    		context.stopService(intent);
    		releaseWifiLock();
    		releaseWakeLock();
    		clearNotification();
    	}
    	
    	private void takeWakeLock() {
    		if(wakeLock == null) {
    			PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
    			
    			// Many (all?) devices seem to not properly honor a PARTIAL_WAKE_LOCK,
    			// which should prevent CPU throttling. This has been 
    			// well-complained-about on android-developers.
    			// For these devices, we have a config option to force the phone into a 
    			// full wake lock.
    			if(fullWake) {
    				wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, 
    						WAKE_LOCK_TAG);
    			} else {
    				wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 
    						WAKE_LOCK_TAG);
    			}
    			wakeLock.setReferenceCounted(false);
    		}
    		myLog.d("Acquiring wake lock");
    		wakeLock.acquire();
    	}
    	
    	private void releaseWakeLock() {
    		myLog.d("Releasing wake lock");
    		if(wakeLock != null) {
    			wakeLock.release();
    			wakeLock = null;
    			myLog.d("Finished releasing wake lock");
    		} else {
    			myLog.i("Couldn't release null wake lock");
    		}
    	}
    	
    	private void takeWifiLock() {
    		myLog.d("Taking wifi lock");
    		if(wifiLock == null) {
    			WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    			wifiLock = manager.createWifiLock("SwiFTP");
    			wifiLock.setReferenceCounted(false);
    		}
    		wifiLock.acquire();
    	}
    	
    	private void releaseWifiLock() {
    		myLog.d("Releasing wifi lock");
    		if(wifiLock != null) {
    			wifiLock.release();
    			wifiLock = null;
    		}
    	}
    	
    	public void errorShutdown() {
    		myLog.l(Log.ERROR, "Service errorShutdown() called");
    		cleanupAndStopService();
    	}
    
    	/**
    	 * Gets the IP address of the wifi connection.
    	 * @return The integer IP address if wifi enabled, or null if not.
    	 */
    	public static InetAddress getWifiIp() {
    		Context myContext = Globals.getContext();
    		if(myContext == null) {
    			throw new NullPointerException("Global context is null");
    		}
    		WifiManager wifiMgr = (WifiManager)myContext
    		                        .getSystemService(Context.WIFI_SERVICE);
    		if(isWifiEnabled()) {
    			int ipAsInt = wifiMgr.getConnectionInfo().getIpAddress();
    			if(ipAsInt == 0) {
    				return null;
    			} else {
    				return Util.intToInet(ipAsInt);
    			}
    		} else {
    			return null;
    		}
    	}
    	
    	public static boolean isWifiEnabled() {
    		Context myContext = Globals.getContext();
    		if(myContext == null) {
    			throw new NullPointerException("Global context is null");
    		}
    		WifiManager wifiMgr = (WifiManager)myContext
    		                        .getSystemService(Context.WIFI_SERVICE);
    		if(wifiMgr.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
    			return true;
    		} else {
    			return false;
    		}
    	}
    	
    	public static List<String> getSessionMonitorContents() {
    		return new ArrayList<String>(sessionMonitor);
    	}
    	
    	public static List<String> getServerLogContents() {
    		return new ArrayList<String>(serverLog);
    	}
    	
    	public static void log(int msgLevel, String s) {
    		serverLog.add(s);
    		int maxSize = Defaults.getServerLogScrollBack();
    		while(serverLog.size() > maxSize) {
    			serverLog.remove(0);
    		}
    		//updateClients();
    	}
    	
    	public static void updateClients() {
    		UiUpdater.updateClients();
    	}
    	
    	public static void writeMonitor(boolean incoming, String s) {}
    
    
    	public static int getPort() {
    		return port;
    	}
    
    	public static void setPort(int port) {
    		FTPServerService.port = port;
    	}
    
    	/**
    	 * The FTPServerService must know about all running session threads so they
    	 * can be terminated on exit. Called when a new session is created.
    	 */
    	public void registerSessionThread(SessionThread newSession) {
    		// Before adding the new session thread, clean up any finished session
    		// threads that are present in the list.
    		
    		// Since we're not allowed to modify the list while iterating over
    		// it, we construct a list in toBeRemoved of threads to remove
    		// later from the sessionThreads list.
    		synchronized(this) {
    			List <SessionThread> toBeRemoved = new ArrayList<SessionThread>();
    			for(SessionThread sessionThread : sessionThreads) {
    				if(!sessionThread.isAlive()) {
    					myLog.l(Log.DEBUG, "Cleaning up finished session...");
    					try {
    						sessionThread.join();
    						myLog.l(Log.DEBUG, "Thread joined");
    						toBeRemoved.add(sessionThread);
    						sessionThread.closeSocket(); // make sure socket closed
    					} catch (InterruptedException e) {
    						myLog.l(Log.DEBUG, "Interrupted while joining");
    						// We will try again in the next loop iteration
    					}
    				}
    			}
    			for(SessionThread removeThread : toBeRemoved) {
    				sessionThreads.remove(removeThread);
    			}
    			
    			// Cleanup is complete. Now actually add the new thread to the list.
    			sessionThreads.add(newSession);
    		}
    		myLog.d("Registered session thread");
    	}
    
    	static public SharedPreferences getSettings() {
    		return settings;
    	}
    }
    

    注:
    (1)、以上作修改部分的代码。
    (2)、代码中使用到的图片没有贴上,有需要的可以自行补上。
    (3)、去除了Proxy部分。

    以上就是基于SwiFTP的实现。
    转载请注明出处:


    转载于:https://www.cnblogs.com/sparkleDai/p/7605030.html

    展开全文
  • ftp手机客户端 可以上传手机文件以及下载电脑客户端文件 安卓版本 可以渲染服务器端文件成列表化查看,并且点击文件可以获得文件路径,点击文件夹可以继续查看这个文件夹下面有什么文件 可以显示下载和上传的进度 ...
  • Android FTP实现

    2021-02-08 19:33:06
    Android基于SwiFTP开源库的FTP实现(FTP匿名登录) Android开发_swiftp开源ftp服务端移植 Android中FTP服务器、客户端搭建以及SwiFTP、ftp4j介绍 实现 其实就是想通过wifi从手机拷贝文件,自己手动编译上述开源代码也...

    作者

    QQ群:852283276
    微信:arm80x86
    微信公众号:青儿创客基地
    B站:主页 https://space.bilibili.com/208826118

    参考

    Android基于SwiFTP开源库的FTP实现(FTP匿名登录)
    Android开发_swiftp开源ftp服务端移植
    Android中FTP服务器、客户端搭建以及SwiFTP、ftp4j介绍

    实现

    其实就是想通过wifi从手机拷贝文件,自己手动编译上述开源代码也是可以的,但是麻烦,也没装android studio环境,到小米应用商店找到了一款实简FTP,就是开源的SwiFTP界面,基本没做什么改动。

    展开全文
  • 我们在新版本中添加了Android/iOS的App, 便于手机用户传输文件. 可编程的事件管理器 事件触发时, 可执行Lua脚本, 发送电子邮件 或 执行第三方的应用程序. 负载均衡 & 高可用 可以使用Wing Gateway对WingFTP进行...
  • net2ftp 1.3

    2021-04-30 23:10:20
    net2ftp是一个基于web的FTP服务,只要有浏览器,不必装任何客户端就能随时进行文件的上传和下载服务,界面...改进:皮肤已针对手机和平板电脑以及从右到左语言进行了优化;错误修正:现在在登录屏幕上填写了默认端口号。
  • 这是一款实现了可以支持ftp协议的功能源码,该功能基于socket实现ftp协议,可以用于实现在电脑中远程管理手机文件。该代码中支持常用的一些ftp命令,共29个。支持匿名登录、非匿名登录。命令与方法名的映射在ftp_...
  • 1.登录阿里云 配置相关 手机版本阿里云app有bug!!! 无法淘宝授权 首先点击控制台 点击这个1 然后我们点击管理,进入后台 在这里我们可以设置密码,这个密码是我们用来 x-shell或者ftp使用...
  • 实现背景 近期接触到一个需求,就是将文件从Android系统上传到FTP服务器,虽然之前接触过FTP服务器,了解基本的使用流程,但是将此流程...使用用户名和密码登录FTP服务器 4.更改目录,将当前客户端指向的服务端的...
  • 用filezilla搭建FTP时的一个细节 最近用自己的Linux主机搭建FTP,...不过我遇到了一个问题:用手机平板通过设置的ftp账号登录后,默认的根目录就是你的FTP_user的目录(/home/FTP_user),问题是你怎么能访问到其他...
  • 为了方便在Android手机和PC电脑之间文件传输和管理,于是想在Android上建立FTP服务器来实现文件访问及管理操作。从网上找到这个例子后开始使用,结果发现,死活都登录不了,配置不了用户名和密码,匿名登录也登不上...
  • FTP文件管理项目(本地云)项目日报(一)会议记录项目甘特图 会议记录 1.各成员的互相介绍和认识 2.讲解各负责端的需求,和功能开发 3.需求分析(客户端) 功能 内容 系统启动 本地云启动,初始化界面,软件...
  • 最近有同事在使用Android 4.0.4手机的“tethering” 时遇到了一个问题--FTP可以登录成功,但是无法查看文件“dir , ls”,自己验证了一下,无论是Windows XP, Win 7都不能工作,google一把,发现windows只支持主动...
  • 完成后在电脑带成功进入手机的文件管理界面,但想要打开一个文件夹时出现了“ftp无法访问此文件夹,请确保输入的文件名是正确” 这时就需要打开控制面板,找到:程序和功能–>启动或关闭windows功能,点进去,...
  • 为了实现基于Android手机平台的音频会话和图像上传,设计了一个基于Android手机的...测试结果表明,用户可以通过软件客户端与PC客户端进行音频会话,并有良好的通话体验,登录FTP服务器可以查看到通过软件上传的图像。
  • FTPCE 2.2 WM手机

    2010-07-20 12:15:49
    3.记下手机上显示的FTP服务地址,在PC电脑上打开资源管理器,输入该地址,登录手机FTP服务; 4.这时就可以在PC电脑与手机之间复制粘贴文件了。 20100720 V2.2: 新版本改进在闲时无法保持屏幕常亮的问题。
  • /* 合法url */ export function validateURL ... const urlregex = /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?/ // const urlregex = /^(https?|f...
  • 2.将【oksvn_ad】文件夹通过FTP工具上传到自己的网站空间根目录下的/source/plugin/下 3.使用自己论坛网站管理员账户登录后台,点击【应用】-【插件】; 4.在最底部有一个【未安装插件】,找到【手机版广告位 1.0】...
  • 3、修改手机登录、注册页面的样式。4、修改手机版首页、帖子、搜索样式。5、修改全站上传图片的方式,先在本地用JS压缩转换成base64格式,再压缩转成实体图片,解决了手机即时拍照图片太大无法上传的问题。6、修复...
  • 常用端口简介 ...文件传输协议)服务,FTP服务主要是为了在两台计算机之间实现文件的上传与下载,一台计算机作为FTP客户端,另一台计算机作为FTP服务器,可以采用匿名(anonymous)登录和授权用户名与密码
  • 3.最后:模板成功安装好后,为了您的网站安全一定要在ftp中把 “diguo”文件夹删除。   手机版:后面加/wap 仿58团购程序多城市 手机版2.0更新 网站框架采用大气58团购架构,外表大气美观,提升了网站速度效率 ...
  • 我们以站库网为例,说明宝塔面板建立手机子目录。...2, 登录宝塔面板后台,在已经开设了网站的设置中加入手机版。 点设置–找到弹出的对话框,看下图设置便可,是不是很简单。了解更多建站资讯,更多免费建站源码可...
  • 集成 Spring Security, 支持手机验证码和用户密码登录,使用JWT 配置 Spring Data Redis 或 使用 Spring Cache 管理缓存 修改所有接口为 RESTFull 风格, 接口成功时只返回数据,错误时通过HTTP状态码和业务状态码处理...
  • 1、不需要通过FTP找到文件修改代码进行配置,新增了安装程序install,新网站上传程序到根目录后,运行手机版域名,自动跳转到安装页面,该填的信息直接在安装的时候填写完毕。 2、安全封装,虽然新增了安装程序,...
  • 手机3G网站建设系统

    2013-01-31 16:26:56
    解压、用FTP上传到你的网站空间中 2、3GCMS支持根目录和子目录。建议放置于根目录,如果你的网站空间已有网站,可以新建一个子目录,将文件解压到子目录中。 3、输入地址:http://***安装目录****/lifeadmin.进入后台...

空空如也

空空如也

1 2 3 4 5 ... 9
收藏数 164
精华内容 65
关键字:

手机登录ftp