package com.kth.baasio.entity.file;
import static com.kth.common.utils.LogUtils.makeLogTag;
import static org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion.NON_NULL;
import com.kth.baasio.Baas;
import com.kth.baasio.BuildConfig;
import com.kth.baasio.callback.BaasioAsyncTask;
import com.kth.baasio.callback.BaasioCallback;
import com.kth.baasio.callback.BaasioDownloadAsyncTask;
import com.kth.baasio.callback.BaasioDownloadCallback;
import com.kth.baasio.callback.BaasioUploadAsyncTask;
import com.kth.baasio.callback.BaasioUploadCallback;
import com.kth.baasio.entity.BaasioBaseEntity;
import com.kth.baasio.entity.BaasioConnectableEntity;
import com.kth.baasio.entity.file.multipart.FilePart;
import com.kth.baasio.entity.file.multipart.FilePart.BaasioProgressListener;
import com.kth.baasio.entity.file.multipart.MultipartEntity;
import com.kth.baasio.entity.file.multipart.StringPart;
import com.kth.baasio.exception.BaasioError;
import com.kth.baasio.exception.BaasioException;
import com.kth.baasio.response.BaasioResponse;
import com.kth.baasio.utils.JsonUtils;
import com.kth.baasio.utils.ObjectUtils;
import com.kth.common.utils.LogUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.apache.http.util.EntityUtils;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import android.net.Uri;
import android.util.Log;
import android.webkit.MimeTypeMap;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class BaasioFile extends BaasioConnectableEntity {
private static final String TAG = makeLogTag(BaasioFile.class);
public final static String ENTITY_TYPE = "file";
public final static String PROPERTY_FILENAME = "filename";
public final static String PROPERTY_CONTENT_LENGTH = "content-length";
public final static String PROPERTY_CONTENT_TYPE = "content-type";
public final static String PROPERTY_CHECKSUM = "checksum";
public final static String PROPERTY_ETAG = "etag";
public final static String PROPERTY_SIZE = "size";
public BaasioFile() {
super();
setType(ENTITY_TYPE);
}
public BaasioFile(BaasioConnectableEntity entity) {
super(entity);
}
/**
* Get predefined property names.
*
* @return List of predefined property names
*/
@Override
@JsonIgnore
public List<String> getPropertyNames() {
List<String> properties = super.getPropertyNames();
properties.add(PROPERTY_FILENAME);
properties.add(PROPERTY_CONTENT_LENGTH);
properties.add(PROPERTY_CONTENT_TYPE);
properties.add(PROPERTY_CHECKSUM);
properties.add(PROPERTY_ETAG);
properties.add(PROPERTY_SIZE);
return properties;
}
/**
* Get file name.
*
* @return File name
*/
@JsonSerialize(include = NON_NULL)
public String getFilename() {
return JsonUtils.getStringProperty(properties, PROPERTY_FILENAME);
}
/**
* Set file name.
*
* @param filename File name
*/
public void setFilename(String filename) {
JsonUtils.setStringProperty(properties, PROPERTY_FILENAME, filename);
}
/**
* Get content length.
*
* @return Content length
*/
@JsonSerialize(include = NON_NULL)
@JsonProperty(PROPERTY_CONTENT_LENGTH)
public Long getContentLength() {
return JsonUtils.getLongProperty(properties, PROPERTY_CONTENT_LENGTH);
}
/**
* Set content length.
*
* @param contentLength content-length
*/
public void setContentLength(long contentLength) {
JsonUtils.setLongProperty(properties, PROPERTY_CONTENT_LENGTH, contentLength);
}
/**
* Get content type.
*
* @return Content type
*/
@JsonSerialize(include = NON_NULL)
@JsonProperty(PROPERTY_CONTENT_TYPE)
public String getContentType() {
return JsonUtils.getStringProperty(properties, PROPERTY_CONTENT_TYPE);
}
/**
* Set content type.
*
* @param contentType content-type
*/
public void setContentType(String contentType) {
JsonUtils.setStringProperty(properties, PROPERTY_CONTENT_TYPE, contentType);
}
/**
* Get checksum.
*
* @return Checksum
*/
@JsonSerialize(include = NON_NULL)
@JsonProperty(PROPERTY_CHECKSUM)
public String getChecksum() {
return JsonUtils.getStringProperty(properties, PROPERTY_CHECKSUM);
}
/**
* Set checksum.
*
* @param checksum Checksum
*/
public void setChecksum(String checksum) {
JsonUtils.setStringProperty(properties, PROPERTY_CHECKSUM, checksum);
}
/**
* Get etag.
*
* @return etag
*/
@JsonSerialize(include = NON_NULL)
@JsonProperty(PROPERTY_ETAG)
public String getEtag() {
return JsonUtils.getStringProperty(properties, PROPERTY_ETAG);
}
/**
* Set etag.
*
* @param etag ETAG
*/
public void setEtag(String etag) {
JsonUtils.setStringProperty(properties, PROPERTY_ETAG, etag);
}
/**
* Get etag.
*
* @return etag
*/
@JsonSerialize(include = NON_NULL)
@JsonProperty(PROPERTY_SIZE)
public Long getSize() {
return JsonUtils.getLongProperty(properties, PROPERTY_SIZE);
}
/**
* Set etag.
*
* @param size Size
*/
public void setSize(long size) {
JsonUtils.setLongProperty(properties, PROPERTY_SIZE, size);
}
private static String getMimeType(File file) {
String url = file.getAbsolutePath();
String type = null;
int dotPos = url.lastIndexOf(".") + 1;
String extension = url.substring(dotPos);
if (extension != null) {
MimeTypeMap mime = MimeTypeMap.getSingleton();
type = mime.getMimeTypeFromExtension(extension);
}
return type;
}
/**
* Get file entity.
*
* @return Entity
*/
public BaasioFile get() throws BaasioException {
BaasioBaseEntity entity = BaasioBaseEntity.get(getType(), getUniqueKey());
return entity.toType(BaasioFile.class);
}
/**
* Get file entity. Executes asynchronously in background and the callbacks
* are called in the UI thread.
*
* @param callback Result callback
*/
public void getInBackground(final BaasioCallback<BaasioFile> callback) {
(new BaasioAsyncTask<BaasioFile>(callback) {
@Override
public BaasioFile doTask() throws BaasioException {
return BaasioFile.this.get();
}
}).execute();
}
/**
* Save file entity to baas.io.
*
* @return Saved entity
*/
public BaasioFile save() throws BaasioException {
if (ObjectUtils.isEmpty(getType())) {
throw new IllegalArgumentException(BaasioError.ERROR_MISSING_TYPE);
}
if (ObjectUtils.isEmpty(getFilename())) {
throw new IllegalArgumentException(BaasioError.ERROR_MISSING_FILE_NAME);
}
BaasioResponse response = Baas.io().apiRequest(HttpMethod.POST, null, this, getType());
if (response != null) {
BaasioFile entity = response.getFirstEntity().toType(BaasioFile.class);
if (!ObjectUtils.isEmpty(entity)) {
return entity;
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NORESULT_ENTITY);
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NO_RESPONSE_DATA);
}
/**
* Save file entity to baas.io. Executes asynchronously in background and
* the callbacks are called in the UI thread.
*
* @param callback Result callback
*/
public void saveInBackground(final BaasioCallback<BaasioFile> callback) {
(new BaasioAsyncTask<BaasioFile>(callback) {
@Override
public BaasioFile doTask() throws BaasioException {
return save();
}
}).execute();
}
/**
* Update file entity from baas.io.
*
* @return Updated entity
*/
public BaasioFile update() throws BaasioException {
if (ObjectUtils.isEmpty(getType())) {
throw new IllegalArgumentException(BaasioError.ERROR_MISSING_TYPE);
}
if (ObjectUtils.isEmpty(getFilename())) {
throw new IllegalArgumentException(BaasioError.ERROR_MISSING_FILE_NAME);
}
BaasioResponse response = Baas.io().apiRequest(HttpMethod.PUT, null, this, getType(),
getUniqueKey());
if (response != null) {
BaasioFile entity = response.getFirstEntity().toType(BaasioFile.class);
if (!ObjectUtils.isEmpty(entity)) {
return entity;
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NORESULT_ENTITY);
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NO_RESPONSE_DATA);
}
/**
* Update file entity from baas.io. Executes asynchronously in background
* and the callbacks are called in the UI thread.
*
* @param callback Result callback
*/
public void updateInBackground(final BaasioCallback<BaasioFile> callback) {
(new BaasioAsyncTask<BaasioFile>(callback) {
@Override
public BaasioFile doTask() throws BaasioException {
return update();
}
}).execute();
}
/**
* Delete file entity from baas.io.
*
* @return Deleted entity
*/
public BaasioFile delete() throws BaasioException {
if (ObjectUtils.isEmpty(getType())) {
throw new IllegalArgumentException(BaasioError.ERROR_MISSING_TYPE);
}
BaasioResponse response = Baas.io().apiRequest(HttpMethod.DELETE, null, this, getType(),
getUniqueKey());
if (response != null) {
BaasioFile entity = response.getFirstEntity().toType(BaasioFile.class);
if (!ObjectUtils.isEmpty(entity)) {
return entity;
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NORESULT_ENTITY);
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NO_RESPONSE_DATA);
}
/**
* Delete file entity from baas.io. Executes asynchronously in background
* and the callbacks are called in the UI thread.
*
* @param callback Result callback
*/
public void deleteInBackground(final BaasioCallback<BaasioFile> callback) {
(new BaasioAsyncTask<BaasioFile>(callback) {
@Override
public BaasioFile doTask() throws BaasioException {
return delete();
}
}).execute();
}
/**
* Create a file entity and upload file content to baas.io. Executes
* asynchronously in background and the callbacks are called in the UI
* thread.
*
* @param localFilePath Source local file path. This must not be null.
* @return The uploading task to cancel.
*/
public BaasioUploadAsyncTask fileUploadInBackground(final String localFilePath,
final String destFileName, final BaasioUploadCallback callback) {
BaasioUploadAsyncTask task = new BaasioUploadAsyncTask(callback) {
private FilePart filePart = null;
@Override
public BaasioFile doTask() throws BaasioException {
if (ObjectUtils.isEmpty(localFilePath)) {
throw new IllegalArgumentException(BaasioError.ERROR_MISSING_LOCAL_FILE_PATH);
}
File file = new File(localFilePath);
if (!file.exists()) {
throw new IllegalArgumentException(BaasioError.ERROR_FILE_IS_NOT_EXIST);
}
StringPart stringPart = new StringPart("entity",
JsonUtils.toJsonString(BaasioFile.this), "UTF-8");
if (BuildConfig.DEBUG) {
LogUtils.LOGV(TAG, "StringPart:\n" + stringPart.toString());
}
final long fileSize = file.length();
filePart = new FilePart("file", file, destFileName, getMimeType(file),
new BaasioProgressListener() {
@Override
public void updateTransferred(long transferedBytes) {
publishProgress(fileSize, transferedBytes);
if (isCancelled()) {
filePart.cancel();
Log.e("upload", "cancel");
}
}
});
if (BuildConfig.DEBUG) {
LogUtils.LOGV(TAG, "FilePart:\n" + filePart.toString());
}
MultipartEntity multipartEntity = new MultipartEntity();
multipartEntity.addPart(stringPart);
multipartEntity.addPart(filePart);
HttpResponse httpResponse = Baas.io().uploadFileRequest(HttpMethod.POST,
multipartEntity, "files");
if (!ObjectUtils.isEmpty(httpResponse)) {
if (!ObjectUtils.isEmpty(httpResponse.getStatusLine())) {
if (httpResponse.getStatusLine().getStatusCode() >= 200
&& httpResponse.getStatusLine().getStatusCode() < 300) {
String body = null;
try {
body = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
} catch (ParseException e) {
throw new BaasioException(e);
} catch (IOException e) {
throw new BaasioException(e);
}
if (!ObjectUtils.isEmpty(body)) {
LogUtils.LOGV(TAG, "Client.httpRequest(): reponse body: " + body);
BaasioResponse response = JsonUtils.parse(body,
BaasioResponse.class);
if (response != null) {
if (response.getFirstEntity() != null) {
BaasioBaseEntity entity = response.getFirstEntity();
return entity.toType(BaasioFile.class);
}
throw new BaasioException(
BaasioError.ERROR_UNKNOWN_NORESULT_ENTITY);
}
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NO_RESPONSE_DATA);
}
HttpStatus status = HttpStatus.valueOf(httpResponse.getStatusLine()
.getStatusCode());
String body = null;
try {
body = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
} catch (ParseException e) {
throw new BaasioException(e);
} catch (IOException e) {
throw new BaasioException(e);
}
if (!ObjectUtils.isEmpty(body)) {
BaasioException baasioError = new BaasioException(status, body);
if (HttpStatus.UNAUTHORIZED.toString().equals(
baasioError.getStatusCode())) {
LogUtils.LOGV(TAG, "Need Login");
Baas.io().fireUnauthorized();
}
throw baasioError;
}
throw new BaasioException(status, "");
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NO_STATUSLINE);
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NO_HTTP_RESPONSE_DATA);
}
};
task.execute();
return task;
}
/**
* Update file entity and change file content to baas.io. UUID must not be
* null. Executes asynchronously in background and the callbacks are called
* in the UI thread.
*
* @param localFilePath Source local file path. This must not be null.
* @return The uploading task to cancel.
*/
public BaasioUploadAsyncTask fileUpdateInBackground(final String localFilePath,
final String destFileName, final BaasioUploadCallback callback) {
BaasioUploadAsyncTask task = new BaasioUploadAsyncTask(callback) {
private FilePart filePart = null;
@Override
public BaasioFile doTask() throws BaasioException {
if (ObjectUtils.isEmpty(getUniqueKey())) {
throw new IllegalArgumentException(BaasioError.ERROR_MISSING_UUID_OR_NAME);
}
if (ObjectUtils.isEmpty(localFilePath)) {
throw new IllegalArgumentException(BaasioError.ERROR_MISSING_LOCAL_FILE_PATH);
}
File file = new File(localFilePath);
if (!file.exists()) {
throw new IllegalArgumentException(BaasioError.ERROR_FILE_IS_NOT_EXIST);
}
StringPart stringPart = new StringPart("entity",
JsonUtils.toJsonString(BaasioFile.this), "UTF-8");
if (BuildConfig.DEBUG) {
LogUtils.LOGV(TAG, "StringPart:\n" + stringPart.toString());
}
final long fileSize = file.length();
filePart = new FilePart("file", file, destFileName, getMimeType(file),
new BaasioProgressListener() {
@Override
public void updateTransferred(long transferedBytes) {
publishProgress(fileSize, transferedBytes);
if (isCancelled()) {
filePart.cancel();
}
}
});
if (BuildConfig.DEBUG) {
LogUtils.LOGV(TAG, "FilePart:\n" + filePart.toString());
}
MultipartEntity multipartEntity = new MultipartEntity();
multipartEntity.addPart(stringPart);
multipartEntity.addPart(filePart);
HttpResponse httpResponse = Baas.io().uploadFileRequest(HttpMethod.PUT,
multipartEntity, "files", getUuid().toString());
if (!ObjectUtils.isEmpty(httpResponse)) {
if (!ObjectUtils.isEmpty(httpResponse.getStatusLine())) {
if (httpResponse.getStatusLine().getStatusCode() >= 200
&& httpResponse.getStatusLine().getStatusCode() < 300) {
String body = null;
try {
body = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
} catch (ParseException e) {
throw new BaasioException(e);
} catch (IOException e) {
throw new BaasioException(e);
}
if (!ObjectUtils.isEmpty(body)) {
LogUtils.LOGV(TAG, "Client.httpRequest(): reponse body: " + body);
BaasioResponse response = JsonUtils.parse(body,
BaasioResponse.class);
if (response != null) {
if (response.getFirstEntity() != null) {
BaasioBaseEntity entity = response.getFirstEntity();
return entity.toType(BaasioFile.class);
}
throw new BaasioException(
BaasioError.ERROR_UNKNOWN_NORESULT_ENTITY);
}
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NO_RESPONSE_DATA);
}
HttpStatus status = HttpStatus.valueOf(httpResponse.getStatusLine()
.getStatusCode());
String body = null;
try {
body = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
} catch (ParseException e) {
throw new BaasioException(e);
} catch (IOException e) {
throw new BaasioException(e);
}
if (!ObjectUtils.isEmpty(body)) {
BaasioException baasioError = new BaasioException(status, body);
if (HttpStatus.UNAUTHORIZED.toString().equals(
baasioError.getStatusCode())) {
LogUtils.LOGV(TAG, "Need Login");
Baas.io().fireUnauthorized();
}
throw baasioError;
}
throw new BaasioException(status, "");
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NO_STATUSLINE);
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NO_HTTP_RESPONSE_DATA);
}
};
task.execute();
return task;
}
/**
* Download file to the local file path from baas.io. Executes
* asynchronously in background and the callbacks are called in the UI
* thread.
*
* @param localFilePath Destination local file path
* @return The downloading task to cancel.
*/
public BaasioDownloadAsyncTask fileDownloadInBackground(final String localFilePath,
final BaasioDownloadCallback callback) {
BaasioDownloadAsyncTask task = new BaasioDownloadAsyncTask(callback) {
@Override
public String doTask() throws Exception {
if (ObjectUtils.isEmpty(localFilePath)) {
throw new IllegalArgumentException(BaasioError.ERROR_MISSING_LOCAL_FILE_PATH);
}
String destFilePath = localFilePath.replace("\\", "/");
String fileName = "";
String filePath;
if (destFilePath.endsWith("/")) {
filePath = destFilePath.substring(0, destFilePath.length() - 1);
fileName = getFilename();
} else {
Uri uri = Uri.parse(destFilePath);
fileName = uri.getLastPathSegment();
filePath = destFilePath.substring(0, destFilePath.length() - fileName.length()
- 1);
}
File dir = new File(filePath);
if (!dir.exists()) {
dir.mkdirs();
} else {
if (!dir.isDirectory()) {
dir.mkdirs();
}
}
if (ObjectUtils.isEmpty(fileName)) {
throw new IllegalArgumentException(BaasioError.ERROR_MISSING_LOCAL_FILE_NAME);
}
String localResultFilePath = filePath + File.separator + fileName;
File file = new File(localResultFilePath);
file.createNewFile();
HttpResponse httpResponse = null;
httpResponse = Baas.io().downloadFileRequest(HttpMethod.GET, "files",
getUniqueKey());
if (httpResponse != null) {
if (httpResponse.getStatusLine() != null) {
if (httpResponse.getStatusLine().getStatusCode() >= 200
&& httpResponse.getStatusLine().getStatusCode() < 300) {
Header[] clHeaders = httpResponse.getHeaders("Content-Length");
if (clHeaders.length > 0) {
Header header = clHeaders[0];
long totalSize = Long.parseLong(header.getValue());
long downloadedSize = 0;
byte buf[] = new byte[Baas.getDownloadBuffSize()];
int numBytesRead;
if (httpResponse.getEntity() != null) {
InputStream stream = httpResponse.getEntity().getContent();
BufferedOutputStream fos = new BufferedOutputStream(
new FileOutputStream(file));
do {
numBytesRead = stream.read(buf);
if (numBytesRead > 0) {
fos.write(buf, 0, numBytesRead);
downloadedSize += numBytesRead;
if (callback != null) {
callback.onProgress(totalSize, downloadedSize);
}
}
if (isCancelled()) {
if (fos != null) {
fos.flush();
fos.close();
}
if (stream != null) {
stream.close();
}
throw new IOException(
BaasioError.ERROR_FILE_TASK_CANCELLED);
}
} while (numBytesRead > 0);
if (fos != null) {
fos.flush();
fos.close();
}
if (stream != null) {
stream.close();
}
if (totalSize != downloadedSize) {
throw new BaasioException(
BaasioError.ERROR_FILE_SIZE_DIFFERENT);
} else {
return localResultFilePath;
}
} else {
throw new BaasioException(
BaasioError.ERROR_UNKNOWN_NO_RESPONSE_DATA);
}
}
} else {
HttpStatus status = HttpStatus.valueOf(httpResponse.getStatusLine()
.getStatusCode());
String body = null;
try {
body = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
} catch (ParseException e) {
throw new BaasioException(e);
} catch (IOException e) {
throw new BaasioException(e);
}
BaasioException baasioError = new BaasioException(status, body);
if (HttpStatus.UNAUTHORIZED.toString().equals(
baasioError.getStatusCode())) {
LogUtils.LOGV(TAG, "Need Login");
Baas.io().fireUnauthorized();
}
throw baasioError;
}
} else {
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NO_STATUSLINE);
}
}
throw new BaasioException(BaasioError.ERROR_UNKNOWN_NO_HTTP_RESPONSE_DATA);
}
};
task.execute();
return task;
}
}