package com.wouterbreukink.onedrive.client;
import com.google.api.client.http.*;
import com.google.api.client.http.json.JsonHttpContent;
import com.google.api.client.util.Key;
import com.wouterbreukink.onedrive.client.authoriser.AuthorisationProvider;
import com.wouterbreukink.onedrive.client.downloader.ResumableDownloader;
import com.wouterbreukink.onedrive.client.downloader.ResumableDownloaderProgressListener;
import com.wouterbreukink.onedrive.client.facets.FileFacet;
import com.wouterbreukink.onedrive.client.facets.FileSystemInfoFacet;
import com.wouterbreukink.onedrive.client.facets.FolderFacet;
import com.wouterbreukink.onedrive.client.resources.Item;
import com.wouterbreukink.onedrive.client.resources.UploadSession;
import com.wouterbreukink.onedrive.client.serialization.JsonDateSerializer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Date;
import static com.wouterbreukink.onedrive.CommandLineOpts.getCommandLineOpts;
class RWOneDriveProvider extends ROOneDriveProvider implements OneDriveProvider {
public RWOneDriveProvider(AuthorisationProvider authoriser) {
super(authoriser);
}
public OneDriveItem replaceFile(OneDriveItem parent, File file) throws IOException {
if (!parent.isDirectory()) {
throw new IllegalArgumentException("Parent is not a folder");
}
HttpRequest request = requestFactory.buildPutRequest(
OneDriveUrl.putContent(parent.getId(), file.getName()),
new FileContent(null, file));
Item response = request.execute().parseAs(Item.class);
OneDriveItem item = OneDriveItem.FACTORY.create(response);
// Now update the item
BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
return updateFile(item, new Date(attr.creationTime().toMillis()), new Date(attr.lastModifiedTime().toMillis()));
}
public OneDriveItem uploadFile(OneDriveItem parent, File file) throws IOException {
if (!parent.isDirectory()) {
throw new IllegalArgumentException("Parent is not a folder");
}
// Generate the update item
BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
FileSystemInfoFacet fsi = new FileSystemInfoFacet();
fsi.setLastModifiedDateTime(JsonDateSerializer.INSTANCE.serialize(new Date(attr.lastModifiedTime().toMillis())));
fsi.setCreatedDateTime(JsonDateSerializer.INSTANCE.serialize(new Date(attr.creationTime().toMillis())));
WriteItemFacet itemToWrite = new WriteItemFacet(file.getName(), fsi, true, false);
MultipartContent content = new MultipartContent()
.addPart(new MultipartContent.Part(
new HttpHeaders()
.set("Content-ID", "<metadata>")
.setAcceptEncoding(null),
new JsonHttpContent(JSON_FACTORY, itemToWrite)))
.addPart(new MultipartContent.Part(
new HttpHeaders()
.set("Content-ID", "<content>")
.setAcceptEncoding(null),
new FileContent(null, file)));
HttpRequest request = requestFactory.buildPostRequest(
OneDriveUrl.postMultiPart(parent.getId()), content);
request.setLoggingEnabled(true);
return OneDriveItem.FACTORY.create(request.execute().parseAs(Item.class));
}
@Override
public OneDriveUploadSession startUploadSession(OneDriveItem parent, File file) throws IOException {
HttpRequest request = requestFactory.buildPostRequest(
OneDriveUrl.createUploadSession(parent.getId(), file.getName()),
new JsonHttpContent(JSON_FACTORY, new UploadSessionFacet(file.getName())));
UploadSession session = request.execute().parseAs(UploadSession.class);
return new OneDriveUploadSession(parent, file, session.getUploadUrl(), session.getNextExpectedRanges());
}
@Override
public void uploadChunk(OneDriveUploadSession session) throws IOException {
byte[] bytesToUpload = session.getChunk();
OneDriveItem item;
HttpRequest request = requestFactory.buildPutRequest(
new GenericUrl(session.getUploadUrl()),
new ByteArrayContent(null, bytesToUpload));
request.getHeaders().setContentRange(String.format("bytes %d-%d/%d", session.getTotalUploaded(), session.getTotalUploaded() + bytesToUpload.length - 1, session.getFile().length()));
if (session.getTotalUploaded() + bytesToUpload.length < session.getFile().length()) {
UploadSession response = request.execute().parseAs(UploadSession.class);
session.setRanges(response.getNextExpectedRanges());
return;
} else {
item = OneDriveItem.FACTORY.create(request.execute().parseAs(Item.class));
}
// If this is the final chunk then set the properties
BasicFileAttributes attr = Files.readAttributes(session.getFile().toPath(), BasicFileAttributes.class);
item = updateFile(item, new Date(attr.creationTime().toMillis()), new Date(attr.lastModifiedTime().toMillis()));
// Upload session is now complete
session.setComplete(item);
}
public OneDriveItem updateFile(OneDriveItem item, Date createdDate, Date modifiedDate) throws IOException {
FileSystemInfoFacet fileSystem = new FileSystemInfoFacet();
fileSystem.setCreatedDateTime(JsonDateSerializer.INSTANCE.serialize(createdDate));
fileSystem.setLastModifiedDateTime(JsonDateSerializer.INSTANCE.serialize(modifiedDate));
WriteItemFacet updateItem = new WriteItemFacet(item.getName(), fileSystem, false, item.isDirectory());
HttpRequest request = requestFactory.buildPatchRequest(
OneDriveUrl.item(item.getId()),
new JsonHttpContent(JSON_FACTORY, updateItem));
Item response = request.execute().parseAs(Item.class);
return OneDriveItem.FACTORY.create(response);
}
public OneDriveItem createFolder(OneDriveItem parent, File target) throws IOException {
WriteFolderFacet newFolder = new WriteFolderFacet(target.getName());
HttpRequest request = requestFactory.buildPostRequest(
OneDriveUrl.children(parent.getId()),
new JsonHttpContent(JSON_FACTORY, newFolder));
Item response = request.execute().parseAs(Item.class);
OneDriveItem item = OneDriveItem.FACTORY.create(response);
// Set the remote timestamps
BasicFileAttributes attr = Files.readAttributes(target.toPath(), BasicFileAttributes.class);
item = updateFile(item, new Date(attr.creationTime().toMillis()), new Date(attr.lastModifiedTime().toMillis()));
return item;
}
public void download(OneDriveItem item, File target, ResumableDownloaderProgressListener progressListener) throws IOException {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(target);
ResumableDownloader downloader = new ResumableDownloader(HTTP_TRANSPORT, requestFactory.getInitializer());
downloader.setProgressListener(progressListener);
downloader.setChunkSize(getCommandLineOpts().getSplitAfter() * 1024 * 1024);
downloader.download(OneDriveUrl.content(item.getId()), fos);
} catch (IOException e) {
throw new OneDriveAPIException(0, "Unable to download file", e);
} finally {
if (fos != null) {
fos.close();
}
}
}
public void delete(OneDriveItem remoteFile) throws IOException {
HttpRequest request = requestFactory.buildDeleteRequest(OneDriveUrl.item(remoteFile.getId()));
request.execute();
}
static class WriteFolderFacet {
@Key
private String name;
@Key
private FolderFacet folder;
public WriteFolderFacet(String name) {
this.name = name;
this.folder = new FolderFacet();
}
public String getName() {
return name;
}
public FolderFacet getFolder() {
return folder;
}
}
static class WriteItemFacet {
@Key
private final FileSystemInfoFacet fileSystemInfo;
@Key
private String name;
@Key
private FolderFacet folder;
@Key
private FileFacet file;
@Key("@content.sourceUrl")
private String multipart;
public WriteItemFacet(String name, FileSystemInfoFacet fileSystemInfo, boolean multipart, boolean isDirectory) {
this.name = name;
this.fileSystemInfo = fileSystemInfo;
this.multipart = multipart ? "cid:content" : null;
if (isDirectory) {
this.folder = new FolderFacet();
} else {
this.file = new FileFacet();
}
}
}
static class UploadSessionFacet {
@Key
private FileDetail item;
private UploadSessionFacet(String name) {
this.item = new FileDetail(name);
}
public FileDetail getItem() {
return item;
}
public class FileDetail {
@Key
private String name;
@Key("@name.conflictBehavior")
private String conflictBehavior = "replace";
public FileDetail(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
}