需求:下载视频,下载后只能用自己的APP打开,不允许传播

实现思路:服务器加密,下载下来本地解密,由于android播放视频需要解密完成后再播放,这样存在安全问题,所以我就搭建本地服务器,实现视频加密

1.先给出build的内容,搭建本地服务器,我使用的是AndroidAsync开源库实现

compile 'com.koushikdutta.async:androidasync:2.+'

2.话不多说,直接上代码,由于是demo,加密和解密都是本地实现

public class MainActivityextends AppCompatActivityimplements View.OnClickListener, NIOHttpServer.ExistenceListener {
private static final StringTAG ="MainActivity";
private VideoViewmVideoView;
private ButtonmButton1, mButton2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
mButton1.setOnClickListener(this);
mButton2.setOnClickListener(this);
//本地服务器启动
new Thread(new Runnable() {
@Override
public void run() {//开一个线程防止卡住ui线程
NIOHttpServer
.getInstance(MainActivity.this)
.startServer(MainActivity.this);//本地服务器开始启动,传入监听,监听文件是否存在
}
}).start();
}
private void init() {
mVideoView = findViewById(R.id.mVideoView);
mButton1 = findViewById(R.id.mButton1);
mButton2 = findViewById(R.id.mButton2);
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.mButton1://加密
try {
//获取assets目录下面的文件流
InputStream inputStream = getResources().getAssets().open("test.mp4", AssetManager.ACCESS_RANDOM);
File file =new File(Environment.getExternalStorageDirectory().getPath() +"/out.zc");//加密后的文件的路径
BufferedOutputStream out =new BufferedOutputStream(new FileOutputStream(file));
byte[] bytes1 ="qwertyuiopa".getBytes();
//开头写入11个干扰字符 写入后视频打不开
out.write(bytes1);
byte[] bytes =new byte[512];
int l;
//正常的写入视频源文件呢
while ((l = inputStream.read(bytes, 0, bytes.length)) != -1) {
out.write(bytes, 0, l);
}
inputStream.close();
out.close();
Log.e(TAG, "onClick: 完成了加密");
Toast.makeText(MainActivity.this, "完成了加密", Toast.LENGTH_SHORT).show();
}catch (IOException e) {
e.printStackTrace();
}
break;
case R.id.mButton2://播放
test2();
break;
}
}
private void test2() {
String code = UnicodeUtils.string2Unicode("out.zc");
Uri uri = Uri.parse("http://127.0.0.1:5000/" + code);//后面的参数必须进行Unicode编码,防止中文乱码
//设置视频控制器
MediaController controller =new MediaController(this);
mVideoView.setMediaController(controller);
//播放完成回调
mVideoView.setOnCompletionListener(new MyPlayerOnCompletionListener());
//设置视频路径
mVideoView.setVideoURI(uri);
//开始播放视频
mVideoView.start();
mVideoView.requestFocus();
}
@Override
public void fail(String str) {//文件不存在
//todo
}
@Override
public void success() {//成功
//todo
}
class MyPlayerOnCompletionListenerimplements MediaPlayer.OnCompletionListener {
@Override
public void onCompletion(MediaPlayer mp) {
}
}
}
3.本地服务器端的实现
public class NIOHttpServerimplements HttpServerRequestCallback {
private static final StringTAG ="NIOHttpServer";
//当前类的实列 采用用单列
private static NIOHttpServermInstance;
//端口
public static int PORT_LISTEN_DEFALT =5000;
//监听
private ExistenceListenerexistenceListener;
//服务器实列
private AsyncHttpServerserver =new AsyncHttpServer();
//单列模式
public static NIOHttpServergetInstance(Context context1) {
if (mInstance ==null) {//懒汉的写法
// 增加类锁,保证只初始化一次
synchronized (NIOHttpServer.class) {
if (mInstance ==null) {
mInstance =new NIOHttpServer();
}
}
}
return mInstance;
}
//返回错误码枚举
public static enum Status {
REQUEST_OK(200, "请求成功"),
REQUEST_ERROR(500, "请求失败"),
REQUEST_ERROR_API(501, "无效的请求接口"),
REQUEST_ERROR_CMD(502, "无效命令"),
REQUEST_ERROR_DEVICEID(503, "不匹配的设备ID"),
REQUEST_ERROR_ENV(504, "不匹配的服务环境");
private final int requestStatus;
private final Stringdescription;
Status(int requestStatus, String description) {
this.requestStatus = requestStatus;
this.description = description;
}
public StringgetDescription() {
return description;
}
public int getRequestStatus() {
return requestStatus;
}
}
/**
* 开启本地服务
*/
public void startServer(ExistenceListener existenceListener) {
//如果有其他的请求方式,例如下面一行代码的写法
server.addAction("OPTIONS", "[\\d\\D]*", this);
server.get("[\\d\\D]*", this);
server.post("[\\d\\D]*", this);
//服务器监听端口
server.listen(PORT_LISTEN_DEFALT);
//传入监听进来
this.existenceListener = existenceListener;
}
@Override
public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {
//unicode转换
String unicode = request.getPath().replace("/", "\\");
//获取传入的参数
String param = UnicodeUtils.unicode2String(unicode);
Log.d(TAG, "onRequest: " + param +" " + unicode);
//这个是获取header参数的地方,一定要谨记
Multimap headers = request.getHeaders().getMultiMap();
// 获取本地的存文件的目录
String path = Environment.getExternalStorageDirectory().getPath();
//获取文件路径
String filePath = path +"/" + param; // 根据url获取文件路径
if (TextUtils.isEmpty(path)) {
response.send("sd卡没有找到");
return;
}
File file =new File(filePath);
BufferedInputStream stream =null;
FileInputStream inputStream =null;
try {
if (file !=null && file.exists()) {
existenceListener.success();//存在文件
Log.d(TAG, "file path = " + file.getAbsolutePath());
//获取本地文件的输入流
inputStream =new FileInputStream(file);
//干扰字符 “qwertyuiopa”一共有11位
byte[] bytes =new byte[11];
//不要开头写入的字符
inputStream.read(bytes, 0, bytes.length);
//写入的干扰字符
Log.d(TAG, "onRequest: " +new String(bytes));
//出去干扰字符的流
stream =new BufferedInputStream(inputStream);
//写出没有干扰字符的流
response.sendStream(stream, stream.available());
}else {
Log.d(TAG, "file path = " + file.getAbsolutePath() +"的资源不存在");
existenceListener.fail("资源不存在");//不存在文件
}
}catch (IOException e) {
e.printStackTrace();
}
}
interface ExistenceListener {
void fail(String str);
void success();
}
}```