From 2570a103eeac919deba1336375b188ff6c0f1b73 Mon Sep 17 00:00:00 2001 From: vkovtun <viktor.kovtun@id.ethz.ch> Date: Tue, 19 Mar 2024 17:54:51 +0100 Subject: [PATCH] BIS-753: Made file upload work for small files. --- .../ethz/sis/afsclient/client/AfsClient.java | 17 ++++++++++---- .../src/js/api/server-data-store-facade.js | 9 ++++---- .../src/js/demo/server-data-store-client.js | 2 +- .../ch/ethz/sis/afs/api/OperationsAPI.java | 2 +- .../sis/afs/dto/operation/WriteOperation.java | 6 ++--- .../ethz/sis/afs/exception/AFSExceptions.java | 2 +- .../afs/manager/TransactionConnection.java | 6 ++--- .../operation/CreateOperationExecutor.java | 6 ----- .../operation/WriteOperationExecutor.java | 8 +++---- .../java/ch/ethz/sis/shared/io/IOUtils.java | 12 +++++++--- .../sis/afsserver/worker/AbstractProxy.java | 4 ++-- .../afsserver/worker/proxy/AuditorProxy.java | 4 ++-- .../worker/proxy/AuthenticationProxy.java | 4 ++-- .../worker/proxy/AuthorizationProxy.java | 4 ++-- .../sis/afsserver/worker/proxy/LogProxy.java | 6 ++--- .../worker/proxy/ValidationProxy.java | 4 ++-- ui-admin/index.html | 1 - .../data-browser/DataBrowserController.js | 22 ++++++++++--------- 18 files changed, 65 insertions(+), 54 deletions(-) diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java index 3347bb51b79..3cf06bf9715 100644 --- a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java @@ -42,8 +42,6 @@ public final class AfsClient implements PublicAPI, ClientAPI private static final int DEFAULT_TIMEOUT_IN_MILLIS = 30000; - private static final String MD5 = "MD5"; - private final int maxReadSizeInBytes; private final int timeout; @@ -361,7 +359,18 @@ public final class AfsClient implements PublicAPI, ClientAPI { try { - return MessageDigest.getInstance(MD5).digest(data); + return MessageDigest.getInstance("MD5").digest(data); + } catch (Exception exception) + { + throw new RuntimeException(exception); + } + } + + private static byte[] getSHA1(final byte[] data) + { + try + { + return MessageDigest.getInstance("SHA-1").digest(data); } catch (Exception exception) { throw new RuntimeException(exception); @@ -592,7 +601,7 @@ public final class AfsClient implements PublicAPI, ClientAPI final byte[] data = result < fullBuffer.length ? Arrays.copyOf(fullBuffer, result) : fullBuffer; try { - final Boolean writeSuccessful = write(owner, destination, this.offset, data, getMD5(data)); + final Boolean writeSuccessful = write(owner, destination, this.offset, data, getSHA1(data)); if (!writeSuccessful) { hasError.set(true); diff --git a/api-data-store-server-javascript/src/js/api/server-data-store-facade.js b/api-data-store-server-javascript/src/js/api/server-data-store-facade.js index 2cb686c301b..943f0bcbc88 100644 --- a/api-data-store-server-javascript/src/js/api/server-data-store-facade.js +++ b/api-data-store-server-javascript/src/js/api/server-data-store-facade.js @@ -439,16 +439,17 @@ function hex2a(hexx) { * @param {str} owner owner of the file * @param {str} source path to file * @param {int} offset offset from which to start writing - * @param {str} dataBase64 data to write in base64 format + * @param {str} base64Data data to write in base64 format + * @param {str} base64Hash MD5 of the base64 data */ -DataStoreServer.prototype.write = function(owner, source, offset, dataBase64){ +DataStoreServer.prototype.write = function(owner, source, offset, base64Data, base64Hash){ const params = this.fillCommonParameters({ "method": "write", "owner" : owner, "source": source, "offset": offset, - "data": dataBase64, - "md5Hash": btoa(hex2a(md5(atob(dataBase64)))), + "data": base64Data, + "md5Hash": base64Hash }); return this._internal.sendHttpRequest( diff --git a/api-data-store-server-javascript/src/js/demo/server-data-store-client.js b/api-data-store-server-javascript/src/js/demo/server-data-store-client.js index 2fee6a53822..8528af8c83a 100644 --- a/api-data-store-server-javascript/src/js/demo/server-data-store-client.js +++ b/api-data-store-server-javascript/src/js/demo/server-data-store-client.js @@ -193,7 +193,7 @@ window.onload = function() { datastoreServer.write(owner, document.getElementById("fpath").value.trim(), parseInt(document.getElementById("foffset").value.trim()), - btoa(document.getElementById("write-text").value.trim())) + document.getElementById("write-text").value.trim()) .then(() => showEntries()); } }; diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/api/OperationsAPI.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/api/OperationsAPI.java index 7ac51069f51..25ae7e158f6 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/api/OperationsAPI.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/api/OperationsAPI.java @@ -29,7 +29,7 @@ public interface OperationsAPI { @NonNull byte[] read(@NonNull String source, @NonNull long offset, @NonNull int limit) throws Exception; - boolean write(@NonNull String source, @NonNull long offset, @NonNull byte[] data, @NonNull byte[] md5Hash) throws Exception; + boolean write(@NonNull String source, @NonNull long offset, @NonNull byte[] data, @NonNull byte[] hash) throws Exception; boolean delete(@NonNull String source) throws Exception; diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/dto/operation/WriteOperation.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/dto/operation/WriteOperation.java index 8048ff351d3..24ac74e6c31 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/dto/operation/WriteOperation.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/dto/operation/WriteOperation.java @@ -36,17 +36,17 @@ public class WriteOperation implements Operation { private String tempSource; private long offset; private byte[] data; - private byte[] md5Hash; + private byte[] hash; private OperationName name; - public WriteOperation(UUID owner, String source, String tempSource, long offset, byte[] data, byte[] md5Hash) { + public WriteOperation(UUID owner, String source, String tempSource, long offset, byte[] data, byte[] hash) { this.owner = owner; this.locks = List.of(new Lock<>(owner, source, LockType.Exclusive)); this.source = source; this.tempSource = tempSource; this.offset = offset; this.data = data; - this.md5Hash = md5Hash; + this.hash = hash; this.name = OperationName.Write; } } diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/exception/AFSExceptions.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/exception/AFSExceptions.java index c6a89190e62..e209906e182 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/exception/AFSExceptions.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/exception/AFSExceptions.java @@ -40,7 +40,7 @@ public enum AFSExceptions implements ExceptionTemplateHolder { PathInStore( RuntimeException.class, List.of(ClientDeveloperCodingError),10015,"Path given to: %s - In store: %s"), PathInStoreCantBeRelative( RuntimeException.class, List.of(ClientDeveloperCodingError),10016,"Path given to: %s - can't contain '/../': %s"), PathNotStartWithRoot( RuntimeException.class, List.of(ClientDeveloperCodingError),10017,"Path given to: %s - don't starts with root '/' : %s"), - MD5NotMatch( RuntimeException.class, List.of(ClientDeveloperCodingError),10018,"MD5 doesn't match on data given to: %s - for: %s"), + HashNotMatch( RuntimeException.class, List.of(ClientDeveloperCodingError),10018,"Hash does not match data given to: %s - for: %s"), DeadlockDetected( RuntimeException.class, List.of(UserUsageError), 10019,"Deadlock detected, %s is already waiting for %s from %s"), TransactionReuse( RuntimeException.class, List.of(CoreDeveloperCodingError), 10020,"Transaction with uuid: %s and state: %s was going to be reused"); diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java index a02572ce815..6c5d9c32015 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java @@ -284,10 +284,10 @@ public class TransactionConnection implements TransactionalFileSystem { private final Set<String> written = new HashSet<>(); @Override - public boolean write(String source, long offset, byte[] data, byte[] md5Hash) throws Exception { + public boolean write(String source, long offset, byte[] data, byte[] hash) throws Exception { source = getSafePath(OperationName.Write, source); String tempSource = OperationExecutor.getTempPath(transaction, source) + "." + UUID.randomUUID(); - WriteOperation operation = new WriteOperation(transaction.getUuid(), source, tempSource, offset, data, md5Hash); + WriteOperation operation = new WriteOperation(transaction.getUuid(), source, tempSource, offset, data, hash); boolean prepared = prepare(operation, source, null); if (prepared) { written.add(source); @@ -380,7 +380,7 @@ public class TransactionConnection implements TransactionalFileSystem { if (prepared) { if (Objects.requireNonNull(operationName) == OperationName.Write) { - transaction.getOperations().add(((WriteOperation) operation).toBuilder().data(null).md5Hash(null).build()); + transaction.getOperations().add(((WriteOperation) operation).toBuilder().data(null).hash(null).build()); } else { transaction.getOperations().add(operation); diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/CreateOperationExecutor.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/CreateOperationExecutor.java index 1d690946d29..ec852f4be0d 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/CreateOperationExecutor.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/CreateOperationExecutor.java @@ -15,17 +15,11 @@ */ package ch.ethz.sis.afs.manager.operation; -import static ch.ethz.sis.afs.exception.AFSExceptions.MD5NotMatch; import static ch.ethz.sis.afs.exception.AFSExceptions.PathInStore; -import static ch.ethz.sis.afs.exception.AFSExceptions.PathIsDirectory; -import java.util.Arrays; - -import ch.ethz.sis.afs.api.dto.File; import ch.ethz.sis.afs.dto.Transaction; import ch.ethz.sis.afs.dto.operation.CreateOperation; import ch.ethz.sis.afs.dto.operation.OperationName; -import ch.ethz.sis.afs.dto.operation.WriteOperation; import ch.ethz.sis.afs.exception.AFSExceptions; import ch.ethz.sis.shared.io.IOUtils; import lombok.NonNull; diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/WriteOperationExecutor.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/WriteOperationExecutor.java index 97ab162e88a..a653ca089cf 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/WriteOperationExecutor.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/WriteOperationExecutor.java @@ -15,7 +15,7 @@ */ package ch.ethz.sis.afs.manager.operation; -import static ch.ethz.sis.afs.exception.AFSExceptions.MD5NotMatch; +import static ch.ethz.sis.afs.exception.AFSExceptions.HashNotMatch; import static ch.ethz.sis.afs.exception.AFSExceptions.PathIsDirectory; import java.util.Arrays; @@ -63,9 +63,9 @@ public class WriteOperationExecutor implements OperationExecutor<WriteOperation> } // 1. Validate new data - byte[] md5Hash = IOUtils.getMD5(operation.getData()); - if (!Arrays.equals(md5Hash, operation.getMd5Hash())) { - AFSExceptions.throwInstance(MD5NotMatch, OperationName.Write.name(), operation.getSource()); + byte[] hash = IOUtils.getSHA1(operation.getData()); + if (!Arrays.equals(hash, operation.getHash())) { + AFSExceptions.throwInstance(HashNotMatch, OperationName.Write.name(), operation.getSource()); } // 2. Create temporary file if it has not been created already boolean tempSourceExists = IOUtils.exists(operation.getTempSource()); diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/io/IOUtils.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/io/IOUtils.java index ee574c4219d..f594ac6d9a6 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/io/IOUtils.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/io/IOUtils.java @@ -620,11 +620,17 @@ public class IOUtils { return pathAStore.equals(pathBStore); } - private static final String MD5 = "MD5"; - public static byte[] getMD5(byte[] data) { try { - return MessageDigest.getInstance(MD5).digest(data); + return MessageDigest.getInstance("MD5").digest(data); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } + + public static byte[] getSHA1(byte[] data) { + try { + return MessageDigest.getInstance("SHA-1").digest(data); } catch (Exception exception) { throw new RuntimeException(exception); } diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/AbstractProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/AbstractProxy.java index 6e075316ec5..aa1c65c7c37 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/AbstractProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/AbstractProxy.java @@ -144,8 +144,8 @@ public abstract class AbstractProxy implements Worker<TransactionalFileSystem> { } @Override - public Boolean write(@NonNull String sourceOwner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] md5Hash) throws Exception { - return nextProxy.write(sourceOwner, source, offset, data, md5Hash); + public Boolean write(@NonNull String sourceOwner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] hash) throws Exception { + return nextProxy.write(sourceOwner, source, offset, data, hash); } @Override diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuditorProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuditorProxy.java index a9c0fafb1f8..1cb9d071020 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuditorProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuditorProxy.java @@ -94,9 +94,9 @@ public class AuditorProxy extends AbstractProxy { } @Override - public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] md5Hash) throws Exception { + public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] hash) throws Exception { auditBefore(); - return auditAfter(nextProxy.write(owner, source, offset, data, md5Hash)); + return auditAfter(nextProxy.write(owner, source, offset, data, hash)); } @Override diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthenticationProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthenticationProxy.java index af3ca09d9be..985b22cc6e1 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthenticationProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthenticationProxy.java @@ -116,9 +116,9 @@ public class AuthenticationProxy extends AbstractProxy { } @Override - public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] md5Hash) throws Exception { + public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] hash) throws Exception { validateSessionAvailable(); - return nextProxy.write(owner, source, offset, data, md5Hash); + return nextProxy.write(owner, source, offset, data, hash); } @Override diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthorizationProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthorizationProxy.java index 7790fa79bac..a20473747e3 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthorizationProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthorizationProxy.java @@ -60,9 +60,9 @@ public class AuthorizationProxy extends AbstractProxy { } @Override - public Boolean write(String owner, String source, Long offset, byte[] data, byte[] md5Hash) throws Exception { + public Boolean write(String owner, String source, Long offset, byte[] data, byte[] hash) throws Exception { validateUserRights(owner, source, IOUtils.writePermissions, OperationName.Write); - return nextProxy.write(owner, source, offset, data, md5Hash); + return nextProxy.write(owner, source, offset, data, hash); } @Override diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/LogProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/LogProxy.java index d2c6eeac7a6..da76ff85b3f 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/LogProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/LogProxy.java @@ -79,9 +79,9 @@ public class LogProxy extends AbstractProxy { } @Override - public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] md5Hash) throws Exception { - logger.traceAccess(null, owner, source, offset, data.length, md5Hash.length); - return logger.traceExit(nextProxy.write(owner, source, offset, data, md5Hash)); + public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] hash) throws Exception { + logger.traceAccess(null, owner, source, offset, data.length, hash.length); + return logger.traceExit(nextProxy.write(owner, source, offset, data, hash)); } @Override diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ValidationProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ValidationProxy.java index 42007bf196a..25f378c23e0 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ValidationProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ValidationProxy.java @@ -45,8 +45,8 @@ public class ValidationProxy extends AbstractProxy { } @Override - public Boolean write(String owner, String source, Long offset, byte[] data, byte[] md5Hash) throws Exception { - return nextProxy.write(owner, source, offset, data, md5Hash); + public Boolean write(String owner, String source, Long offset, byte[] data, byte[] hash) throws Exception { + return nextProxy.write(owner, source, offset, data, hash); } @Override diff --git a/ui-admin/index.html b/ui-admin/index.html index 0ad80de41a1..db5bd694f55 100644 --- a/ui-admin/index.html +++ b/ui-admin/index.html @@ -40,7 +40,6 @@ onerror="loadError()" ></script> <script src="/openbis/resources/api/v3/require.js"></script> - <script src="/openbis/resources/api/data-store-server/api/md5.js"></script> <script src="/openbis/resources/api/data-store-server/api/server-data-store-facade.js"></script> </head> <body> diff --git a/ui-admin/src/js/components/database/data-browser/DataBrowserController.js b/ui-admin/src/js/components/database/data-browser/DataBrowserController.js index 92d1d0e20e8..d893a3730b8 100644 --- a/ui-admin/src/js/components/database/data-browser/DataBrowserController.js +++ b/ui-admin/src/js/components/database/data-browser/DataBrowserController.js @@ -169,27 +169,29 @@ export default class DataBrowserController extends ComponentController { while (offset < file.size) { const chunkData = await file.slice(offset, offset + CHUNK_SIZE).arrayBuffer() // console.log(`Uploading chunk: ${offset} - Size: ${chunkData.byteLength}`) - await this._uploadChunk(file.name, offset, await this._arrayBufferToBase64(chunkData)) + await this._uploadChunk(file.name, offset, chunkData) offset += CHUNK_SIZE } } async _uploadChunk(source, offset, data) { - // console.log(data) - return await this.component.datastoreServer.write(this.owner, source, offset, data) + const hash = await crypto.subtle.digest("SHA-1", data) + const base64Data = await this._arrayBufferToBase64(data) + const base64Hash = await this._arrayBufferToBase64(hash) + + return await this.component.datastoreServer.write(this.owner, source, offset, base64Data, base64Hash) } async _arrayBufferToBase64(buffer) { return new Promise((resolve, reject) => { - const blob = new Blob([buffer], {type: 'application/octet-stream'}) - const reader = new FileReader() + const blob = new Blob([buffer]); + const reader = new FileReader(); reader.onloadend = () => { - const dataUrl = reader.result - const base64String = dataUrl.split(',')[1] - resolve(base64String) + const base64data = reader.result.split(',')[1]; + resolve(base64data); }; - reader.onerror = reject - reader.readAsDataURL(blob) + reader.onerror = reject; + reader.readAsDataURL(blob); }); } -- GitLab