package com.qiniu.android.collect;
import com.qiniu.android.http.UserAgent;
import com.qiniu.android.storage.UpToken;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* 收集上传信息,发送到后端
*/
public class UploadInfoCollector {
/**
* 单线程任务队列
*/
private static ExecutorService singleServer = null;
private static OkHttpClient httpClient = null;
private final String serverURL;
private final String recordFileName;
private File recordFile = null;
private long lastUpload;// milliseconds
private static UploadInfoCollector httpCollector;
private static UploadInfoCollector uploadCollector;
private static UploadInfoCollector getHttpCollector() {
if (httpCollector == null) {
httpCollector = new UploadInfoCollector("_qiniu_record_file_hu3z9lo7anx03", Config.serverURL);
}
return httpCollector;
}
private static UploadInfoCollector getUploadCollector() {
if (uploadCollector == null) {
uploadCollector = new UploadInfoCollector("_qiniu_record_file_upm6xola4sk3", Config.serverURL2);
}
return uploadCollector;
}
private UploadInfoCollector(String recordFileName, String serverURL) {
this.recordFileName = recordFileName;
this.serverURL = serverURL;
try {
reset0();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 清理操作。
* 若需更改 isRecord、isUpload 的值,请在此方法调用前修改。
*/
public static void clean() {
try {
if (singleServer != null) {
singleServer.shutdown();
}
} catch (Exception e) {
// do nothing
}
singleServer = null;
httpClient = null;
try {
getHttpCollector().clean0();
} catch (Exception e) {
e.printStackTrace();
}
httpCollector = null;
try {
getUploadCollector().clean0();
} catch (Exception e) {
e.printStackTrace();
}
uploadCollector = null;
}
private void clean0() {
try {
if (recordFile != null) {
recordFile.delete();
} else {
new File(getRecordDir(Config.recordDir), recordFileName).delete();
}
} catch (Exception e) {
// do nothing
}
recordFile = null;
}
/**
* 修改记录"是否记录上传信息: isRecord","记录信息所在文件夹: recordDir"配置后,调用此方法重置.
* 上传方式, 时间间隔,文件最大大小,上传阀值等参数修改不用调用此方法.
*
* @throws java.io.IOException
*/
public static void reset() {
try {
getHttpCollector().reset0();
} catch (Exception e) {
e.printStackTrace();
}
try {
getUploadCollector().reset0();
} catch (Exception e) {
e.printStackTrace();
}
}
private void reset0() throws IOException {
if (Config.isRecord) {
initRecordFile(getRecordDir(Config.recordDir));
}
if (!Config.isRecord && singleServer != null) {
singleServer.shutdown();
}
if (Config.isRecord && (singleServer == null || singleServer.isShutdown())) {
singleServer = Executors.newSingleThreadExecutor();
}
}
private File getRecordDir(String recordDir) {
return new File(recordDir);
}
private void initRecordFile(File recordDir) throws IOException {
if (recordDir == null) {
throw new IOException("record's dir is not setted");
}
if (!recordDir.exists()) {
boolean r = recordDir.mkdirs();
if (!r) {
throw new IOException("mkdir failed: " + recordDir.getAbsolutePath());
}
return;
}
if (!recordDir.isDirectory()) {
throw new IOException(recordDir.getAbsolutePath() + " is not a dir");
}
recordFile = new File(recordDir, recordFileName);
}
public static void handleHttp(final UpToken upToken, final RecordMsg record) {
try {
if (Config.isRecord) {
getHttpCollector().handle0(upToken, record);
}
} catch (Throwable t) {
// do nothing
}
}
public static void handleUpload(final UpToken upToken, final RecordMsg record) {
try {
if (Config.isRecord) {
getUploadCollector().handle0(upToken, record);
}
} catch (Throwable t) {
// do nothing
}
}
private void handle0(final UpToken upToken, final RecordMsg record) {
if (singleServer != null && !singleServer.isShutdown()) {
Runnable taskRecord = new Runnable() {
@Override
public void run() {
if (Config.isRecord) {
try {
tryRecode(record.toRecordMsg(), recordFile);
} catch (Throwable t) {
// do nothing
}
}
}
};
singleServer.submit(taskRecord);
// 少几次上传没有影响
if (Config.isUpload && upToken != UpToken.NULL) {
Runnable taskUpload = new Runnable() {
@Override
public void run() {
if (Config.isRecord && Config.isUpload) {
try {
tryUploadAndClean(upToken, recordFile);
} catch (Throwable t) {
// do nothing
}
}
}
};
singleServer.submit(taskUpload);
}
}
}
private void tryRecode(String msg, File recordFile) {
if (Config.isRecord && recordFile.length() < Config.maxRecordFileSize) {
// 追加到文件尾部并换行
writeToFile(recordFile, msg + "\n", true);
}
}
private void tryUploadAndClean(final UpToken upToken, File recordFile) {
if (Config.isUpload && recordFile.length() > Config.uploadThreshold) {
long now = new Date().getTime();
// Config.interval 单位为:分钟
if (now > (lastUpload + Config.interval * 60 * 1000)) {
lastUpload = now;
//同步上传
boolean success = upload(upToken, recordFile);
if (success) {
// 记录文件重置为空
writeToFile(recordFile, "", false);
writeToFile(recordFile, "", false);
}
}
}
}
private static void writeToFile(File file, String msg, boolean isAppend) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file, isAppend);
fos.write(msg.getBytes(Charset.forName("UTF-8")));
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
// do nothing
}
}
}
}
//同步上传
private boolean upload(final UpToken upToken, File recordFile) {
try {
OkHttpClient client = getHttpClient();
RequestBody reqBody = RequestBody.create(MediaType.parse("text/plain"), recordFile);
Request request = new Request.Builder().url(serverURL).
addHeader("Authorization", "UpToken " + upToken.token).
addHeader("User-Agent", UserAgent.instance().getUa(upToken.accessKey)).
post(reqBody).build();
Response res = client.newCall(request).execute();
try {
return isOk(res);
} finally {
try {
res.body().close();
} catch (Exception e) {
}
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private boolean isOk(Response res) {
return res.isSuccessful() && res.header("X-Reqid") != null;
}
private static OkHttpClient getHttpClient() {
if (httpClient == null) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(10, TimeUnit.SECONDS);
builder.readTimeout(15, TimeUnit.SECONDS);
builder.writeTimeout((Config.interval / 2 + 1) * 60 - 10, TimeUnit.SECONDS);
httpClient = builder.build();
}
return httpClient;
}
public static abstract class RecordMsg {
public abstract String toRecordMsg();
}
}