## Android Smb开发笔记

### 一、SMB的基本操作

Android的SMB开发会用到jcifs这个库。这个库提供了SMB的底层操作功能。

根据URI打开一个SMB。

```java
// 指定SMB的路径
String uri = "smb://administrator:351002@192.168.31.52/H/琅琊榜 全54集 1080P/";
// 网络的相关操作必须通过新线程的方式启动，否则会触发Android的错误
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            // 根据uri打开SmbFile
            SmbFile smbFile = new SmbFile(uri);
            String fileName = smbFile.getName();
            Log.d("Test", fileName);
            // 控制台打印SMB路径下的文件
            try{
                String[] FileList = smbFile.list();
                for (String s : FileList) {
                    Log.d("Test", s);
                }
            }catch (SmbException e){
                e.printStackTrace();
            }
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }
}).start();
```

### 二、SMB转HTTP

当我们把SMB的Uri提供给播放器后，播放器会请求GET视频。对于一个完整的视频，Http的请求如下所示。

如果我们在解析请求头的时候没有收到请求的范围信息，那么默认就是返回全部的内容。

```shell
GET  /smb/192.168.31.52/I/XXX.mp4 HTTP/1.1
User-Agent: ExoPlayerTime/1.0 (Linux;Android 12) ExoPlayerLib/2.7.1
Accept-Encoding:identity
Host: 127.0.0.1:2222
Connection: Keep-Alive
```

当我们拖动进度条的时候，此时视频不是从0开始播放，所以视频播放器会请求包含Range的信息：

```Shell
GET  /smb/192.168.31.52/I/XXX.mp4 HTTP/1.1
User-Agent: ExoPlayerTime/1.0 (Linux;Android 12) ExoPlayerLib/2.7.1
Range: bytes=1207598076-
Accept-Encoding:identity
Host: 127.0.0.1:2222
Connection: Keep-Alive
```

上述情况下，如果我们收到包含Range信息的请求时，返回的时候就需要额外指定一些信息。HTTP的返回有如下的规则：

```Java
// 如果用户拖动了进度条，那么HttpRequest就会包含Range信息
// 1.此时contentLength = end - st, 需要返回Content-Length:头
"Content-Length: " + contentLength + "\r\n"
// 2.此时请求的是部分的信息，所以返回的Responce的类型是206，表示部分内容
HttpStatus.PARTIAL_CONTENT = 206
```



```Java
// 获取文件长度bytes
long contentLen = file.length();
// 从文件获得输入流
InputStream contentIn = file.getInputStream();
// 检查输入数据流是否正常
if (contentLen <= 0 || contentIn == null) {
    httpReq.returnBadRequest();
    return;
}
// 创建Http返回请求
HTTPResponse httpRes = new HTTPResponse();
// 指定视频数据格式
httpRes.setContentType("video/mpeg4");

if(startFrom == 0 && endAt == 0){
    // 请求没有范围信息则返回全部内容，状态OK(200)表示全部内容
    httpRes.setStatusCode(HTTPStatus.OK);
}else if(startFrom > 0){
    // 如果是包含RangeCotent,则需要返回PARTIAL_CONTENT(206)
    httpRes.setStatusCode(HTTPStatus.PARTIAL_CONTENT);
    httpRes.setContentRange(startFrom,endAt, contentLen);
}
// 设置内容总长度
httpRes.setContentLength(contentLen);
// 指定内容数据流
httpRes.setContentInputStream(contentIn);
// HttpReq提交数据
httpReq.post(httpRes);
contentIn.close();
```

HTTP 视频请求

```Java
// 已知信息
File_length = 2794939639
File_Range_st = bytes=2792308814
// 分析
st = 2792308814
// 由于文件长度是2794939639，所以换算的索引是0~2794939639-1
ed = 2794939639 - 1
content_lenght = ( ed - st + 1 )= File_length - 1 - st + 1
= ed - st
    
    
    
```

