sftp 下载实现

使用零拷贝技术优化合并分块的功能
This commit is contained in:
laoyuyu
2020-01-15 20:28:10 +08:00
parent 6f53235807
commit 50b265f22a
37 changed files with 934 additions and 257 deletions

View File

@@ -32,6 +32,7 @@
</intent-filter>
</activity>
<activity android:name=".core.download.SFtpDownloadActivity"/>
<activity android:name=".core.download.DownloadActivity"/>
<activity android:name=".core.download.SingleTaskActivity"/>
<activity android:name=".core.download.mutil.MultiTaskActivity"/>

View File

@@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,8CCEACDEBFE735A306E5E8FA84E90106
s9LGwTfk9PmcqF50jcFNpSB0O93ehxyD9FIahADuPwYnJCEBFQ+Hm06QOagVxB7z
44e23/SCAmo3cWes/IVXUag4+/p8UIcFZc7/CWIBlWaWKuyuP9vD4SD9EiPj6XlC
qaAsYcWxPodLoh4RQP08Chw1TET+XkOQuf1FuOil4eEeJYHBCpILF2I9lv7ujo3m
S3vS/LLpRUISOm7qVskpq0o1EXibdoSRRqvyzl7hzv+4ik7pDBl1Xun+E05So8Bz
aQP6Y8MpuowcFn0Q/OP10vcMm0tT8HwCz2qhfwN+3QQ/MdpePUw9DxCKsFS6cR6u
zILuS4a+/huQ2v2ebgTJK4gH/FYaHDP7jIVLKTB6lu4vxABHOrP19gz/Th7ZA/4i
JW3wR1VmYpSaGYVRnvzQ9sn/sYjntVOcXfOADHxy7VK2k7Ejwot88OJ96kkNCt26
REcb3wM0AFu1sJSpDKLBqAvZEwOMhrbQW6KIorfKLYtywfEkwVdHa8JQY35Z5RrZ
qV5XvdW4LVyD0W4yjaeaeWVDShts2e7T+M9BV7LqYS+IKLrVWdQbn+7ymhi69UT8
mLbiuLshyBDmjMqltS0Wa1Ejd3MTI2pmzgO5G8qTDL7oa2OiUO4aNCy9sy87fogH
iuE+tXrxmFeb8kSmFlblyrOi3EyRSkQEcg+JdooWZMTrxaIQUhYhlA+10JU2pc5w
cPg//c1Z2Hlgrx9tlecxDafPgH7AnLuG6J3AgZBnE1511h21iprnEKafMC9h+Lp8
JmIeFnI3wLgfAz1olKeoRa/aHZGUiiD+GQxI21Q+XEXChAYf9vR7MuKFWqTI8jk7
OfUMPXnKSvPBgTyNyTR8Kf3YBvm0HoWWSQr0YS7CbxsWNzdwpsnKn/qhwdiCt5kK
T1gS+yKTk6F/Z2+D0yBSaopOTumNYEDmDW3Er+r2fvM+UBTYdvYNCkaUysX1BK94
4jmpeZZtagfppNlcQePuPv2zgckBjfeAdn4oQQqwyDS10ovWnTVEqn9yO0njZjXy
PhFZTX2R4Cn6UrW3AlLGCCdl3ogeybuoFF4dYMh6TiMgkD45XbsUrNq+iEBHSiFz
YRK4o40lMGvBxSa2bhZHtyoQlUJkptyFriCt9cH4WDbEIafLe/N29AP7ZUOQUOD8
ZgyyUmER270uBzQig8hRXBJADZXYKqT3hUrosOt64tjw2CNIkMJPz/KVDOM66prb
WiiMV2l+xGnu0tLMCgp7TcCclBEPTlGfpsuP/iFu70j8u4vDjxNwWr3QyuqX0Krs
M6pfLHp4RSfUN9AbrjoSAu4XBVE5d8JNf7YGpx2D+HIV1hAEux9IWj+/lAA9eAEq
DnzI3JeYlWRCCF8Jg2VGt8fwQDsgTtFsHcaCuZqTPXGwsT3EIi/+9wQJshDyP/D4
OWa8HFvkuluLeEDTW7B2u3+AFS01Gt92/fPcY06j+OJL4atW1c5XCF8inlt8Thop
+a33yUHpN8uGzkmKUz/onLxvMLYcTDDAXS2dtslij0ldSNC484GpWPHmLfW1jQCv
99FqBUbzGMy2RHP1xRHvqK2kV1N72QMde5fDLUP+vygtWFh/Nobi/guPR5OuO60R
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBlgTuAhvU/Z0ni2PszmIXkDvANMWNYHfiCfvOxRKoK/jCGmKw/TDF1Z49P5M5CMpKwTaQKDxfolYeCgvH9eBmY1NiE26COWexgoqxrw4pWE9/h2Thx7cVWjGWZZ4iwyYfT19/5SgPNujiW9cWGV9N8VXAKe6Sl7v+6TtjLHNCNEn7B90m6sRIYHXsAQ7W7TBlUD5S6wcAsL6v6RVMVVBoIXykNW+zQK6prAWPCtPWcEw3WwfCIUQfJLS8gMC++Ox/bT9gqR+uZTk8MGxafxa9lrvIvpcZh8uVzaP428E3mlSkJnhTAzk43VoKAKxDA1Z+Qehig7VP9GsSn2HGuYE7 AriaL@DESKTOP-BBML2VA

View File

@@ -38,6 +38,7 @@ import com.arialyy.simple.base.adapter.AbsRVAdapter;
import com.arialyy.simple.base.adapter.RvItemClickSupport;
import com.arialyy.simple.core.download.DownloadActivity;
import com.arialyy.simple.core.download.FtpDownloadActivity;
import com.arialyy.simple.core.download.SFtpDownloadActivity;
import com.arialyy.simple.core.download.group.DownloadGroupActivity;
import com.arialyy.simple.core.download.group.FTPDirDownloadActivity;
import com.arialyy.simple.core.download.m3u8.M3U8LiveDLoadActivity;
@@ -117,6 +118,8 @@ public class MainActivity extends BaseActivity<ActivityMainBinding> {
M3U8LiveDLoadActivity.class);
break;
case 8: // sftp
module.startNextActivity(MainActivity.this, data.get(position),
SFtpDownloadActivity.class);
break;
}
}

View File

@@ -41,7 +41,6 @@ public class FtpDownloadModule extends BaseViewModule {
private DownloadEntity singDownloadInfo;
/**
* xx
* 单任务下载的信息
*/
LiveData<DownloadEntity> getFtpDownloadInfo(Context context) {
@@ -65,6 +64,30 @@ public class FtpDownloadModule extends BaseViewModule {
return liveData;
}
/**
* 单任务下载的信息
*/
LiveData<DownloadEntity> getSftpDownloadInfo(Context context) {
//String url = AppUtil.getConfigValue(context, FTP_URL_KEY, ftpDefUrl);
//String filePath = AppUtil.getConfigValue(context, FTP_PATH_KEY, ftpDefPath);
String url = "ftp://9.9.9.72:22/Cyberduck-6.9.4.30164.zip";
singDownloadInfo = Aria.download(context).getFirstDownloadEntity(url);
if (singDownloadInfo == null) {
singDownloadInfo = new DownloadEntity();
singDownloadInfo.setUrl(url);
String name = getFileName(ftpDefUrl);
singDownloadInfo.setFileName(name);
singDownloadInfo.setFilePath(ftpDefPath + name);
} else {
AppUtil.setConfigValue(context, FTP_PATH_KEY, singDownloadInfo.getFilePath());
AppUtil.setConfigValue(context, FTP_URL_KEY, singDownloadInfo.getUrl());
}
liveData.postValue(singDownloadInfo);
return liveData;
}
/**
* 更新文件保存路径
*

View File

@@ -0,0 +1,232 @@
/*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.simple.core.download;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import com.arialyy.annotations.Download;
import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.common.FtpOption;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.task.DownloadTask;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.FileUtil;
import com.arialyy.frame.util.show.L;
import com.arialyy.frame.util.show.T;
import com.arialyy.simple.R;
import com.arialyy.simple.base.BaseActivity;
import com.arialyy.simple.common.DirChooseDialog;
import com.arialyy.simple.common.ModifyUrlDialog;
import com.arialyy.simple.databinding.ActivitySftpDownloadBinding;
import java.io.File;
import java.io.IOException;
/**
* Created by lyy on 2017/7/25.
* Ftp下载
*/
public class SFtpDownloadActivity extends BaseActivity<ActivitySftpDownloadBinding> {
private String mUrl, mFilePath;
private FtpDownloadModule mModule;
private long mTaskId;
private String user = "tester", passw = "password";
private String prvKeyPath;
private String pubKeyPath;
@Override protected void init(Bundle savedInstanceState) {
super.init(savedInstanceState);
setTitle("FTP文件下载");
Aria.download(this).register();
prvKeyPath = getFilesDir().getPath() + "/id_rsa";
pubKeyPath = getFilesDir().getPath() + "/id_rsa.pub";
copyKey();
mModule = ViewModelProviders.of(this).get(FtpDownloadModule.class);
mModule.getSftpDownloadInfo(this).observe(this, new Observer<DownloadEntity>() {
@Override public void onChanged(@Nullable DownloadEntity entity) {
if (entity == null) {
return;
}
mTaskId = entity.getId();
if (entity.getState() == IEntity.STATE_STOP) {
getBinding().setStateStr(getString(R.string.resume));
} else if (entity.getState() == IEntity.STATE_RUNNING) {
getBinding().setStateStr(getString(R.string.stop));
}
if (entity.getFileSize() != 0) {
getBinding().setFileSize(CommonUtil.formatFileSize(entity.getFileSize()));
getBinding().setProgress(entity.isComplete() ? 100
: (int) (entity.getCurrentProgress() * 100 / entity.getFileSize()));
}
getBinding().setUrl(entity.getUrl());
getBinding().setFilePath(entity.getFilePath());
mUrl = entity.getUrl();
mFilePath = entity.getFilePath();
}
});
getBinding().setViewModel(this);
//try {
// getBinding().codeView.setSource(AppUtil.getHelpCode(this, "FtpDownload.java"));
//} catch (IOException e) {
// e.printStackTrace();
//}
}
private void copyKey() {
try {
// 为了测试方便,每次重新覆盖证书文件
File prvKey = new File(prvKeyPath);
//FileUtil.deleteFile(prvKey);
if (!prvKey.exists()) {
FileUtil.createFileFormInputStream(getAssets().open("id_rsa"), prvKeyPath);
}
File pubKey = new File(pubKeyPath);
//FileUtil.deleteFile(pubKey);
if (!pubKey.exists()) {
FileUtil.createFileFormInputStream(getAssets().open("id_rsa.pub"), pubKeyPath);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.start:
if (mTaskId == -1) {
mTaskId = Aria.download(this).loadFtp(mUrl)
.setFilePath(mFilePath, true)
.sftpOption(getFtpOption())
.create();
getBinding().setStateStr(getString(R.string.stop));
break;
}
if (Aria.download(this).load(mTaskId).isRunning()) {
getBinding().setStateStr(getString(R.string.resume));
Aria.download(this).loadFtp(mTaskId).stop();
} else {
Aria.download(this)
.loadFtp(mTaskId)
.sftpOption(getFtpOption())
.resume();
getBinding().setStateStr(getString(R.string.stop));
}
break;
case R.id.cancel:
Aria.download(this).loadFtp(mTaskId).cancel(true);
getBinding().setStateStr(getString(R.string.start));
mTaskId = -1;
break;
}
}
private FtpOption getFtpOption() {
FtpOption option = new FtpOption();
option.login(user, passw); // 账号密码登录
// 证书登录
option.setPrvKey(prvKeyPath); // 设置私钥
option.setPrvKeyPass("123456"); // 设置私钥密码(如果没有密码,可以不设置)
option.setPubKey(pubKeyPath); // 设置公钥
option.setKnowHostPath(getFilesDir().getPath() + "/know_hosts");
//option.setServerIdentifier(FtpOption.FTPServerIdentifier.SYST_NT);
//option.setConnectionMode(FtpConnectionMode.DATA_CONNECTION_MODE_ACTIVITY);
return option;
}
public void chooseUrl() {
ModifyUrlDialog dialog =
new ModifyUrlDialog(this, getString(R.string.modify_url_dialog_title), mUrl);
dialog.show(getSupportFragmentManager(), "ModifyUrlDialog");
}
public void chooseFilePath() {
DirChooseDialog dirChooseDialog = new DirChooseDialog(this);
dirChooseDialog.show(getSupportFragmentManager(), "DirChooseDialog");
}
@Download.onPre() protected void onPre(DownloadTask task) {
L.d(TAG, "ftp pre");
}
@Download.onTaskPre() protected void onTaskPre(DownloadTask task) {
L.d(TAG, "ftp task pre, fileSize = " + task.getConvertFileSize());
getBinding().setFileSize(task.getConvertFileSize());
}
@Download.onTaskStart() void taskStart(DownloadTask task) {
L.d(TAG, "ftp task create");
}
@Download.onTaskRunning() protected void running(DownloadTask task) {
ALog.d(TAG, "running, p = " + task.getPercent() + ", speed = " + task.getConvertSpeed());
getBinding().setProgress(task.getPercent());
getBinding().setSpeed(task.getConvertSpeed());
}
@Download.onTaskResume() void taskResume(DownloadTask task) {
L.d(TAG, "ftp task resume");
}
@Download.onTaskStop() void taskStop(DownloadTask task) {
L.d(TAG, "ftp task stop");
getBinding().setSpeed("");
getBinding().setStateStr(getString(R.string.resume));
}
@Download.onTaskCancel() void taskCancel(DownloadTask task) {
getBinding().setSpeed("");
getBinding().setProgress(0);
}
@Download.onTaskFail() void taskFail(DownloadTask task) {
L.d(TAG, "ftp task fail");
getBinding().setSpeed("");
getBinding().setStateStr(getString(R.string.resume));
}
@Download.onTaskComplete() void taskComplete(DownloadTask task) {
getBinding().setSpeed("");
getBinding().setProgress(100);
getBinding().setStateStr(getString(R.string.re_start));
Log.d(TAG, "md5 ==> " + CommonUtil.getFileMD5(new File(task.getFilePath())));
T.showShort(this, "文件:" + task.getEntity().getFileName() + ",下载完成");
}
@Override protected int setLayoutId() {
return R.layout.activity_sftp_download;
}
@Override protected void dataCallback(int result, Object data) {
super.dataCallback(result, data);
if (result == ModifyUrlDialog.MODIFY_URL_DIALOG_RESULT) {
mModule.uploadUrl(this, String.valueOf(data));
} else if (result == DirChooseDialog.DIR_CHOOSE_DIALOG_RESULT) {
mModule.updateFilePath(this, String.valueOf(data));
}
}
}

View File

@@ -26,7 +26,6 @@ import android.text.TextUtils;
import androidx.core.content.FileProvider;
import com.arialyy.aria.core.common.AbsEntity;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.FileUtil;
import com.arialyy.simple.BuildConfig;
import java.io.File;
@@ -53,7 +52,7 @@ public class AppUtil {
File ftpCode = new File(path);
if (!ftpCode.exists()) {
FileUtil.createFile(ftpCode);
CommonUtil.createFileFormInputStream(context.getAssets()
FileUtil.createFileFormInputStream(context.getAssets()
.open(String.format("help_code/%s", fileName)),
path);
}

View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto"
>
<data>
<variable
name="fileSize"
type="String"
/>
<variable
name="speed"
type="String"
/>
<variable
name="progress"
type="int"
/>
<variable
name="stateStr"
type="String"
/>
<variable
name="url"
type="String"
/>
<variable
name="filePath"
type="String"
/>
<variable
name="viewModel"
type="com.arialyy.simple.core.download.SFtpDownloadActivity"
/>
</data>
<LinearLayout
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical"
tools:context=".core.download.SingleTaskActivity"
>
<include layout="@layout/layout_bar"/>
<com.arialyy.simple.widget.SvgTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
bind:iconClickListener="@{() -> viewModel.chooseUrl()}"
bind:svg_text_view_icon="@drawable/ic_modify"
bind:text="@{@string/url(url)}"
/>
<com.arialyy.simple.widget.SvgTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
bind:iconClickListener="@{() -> viewModel.chooseFilePath()}"
bind:svg_text_view_icon="@drawable/ic_choose_file"
bind:text="@{@string/file_path(filePath)}"
/>
<include
layout="@layout/layout_content_single"
bind:fileSize="@{fileSize}"
bind:progress="@{progress}"
bind:speed="@{speed}"
bind:stateStr="@{stateStr}"
/>
<!-- <com.arialyy.simple.widget.CodeView-->
<!-- android:id="@+id/code_view"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- />-->
</LinearLayout>
</layout>