fixed bug to upload large file.

This commit is contained in:
Yoshihiro OKUMURA 2024-07-04 12:33:57 +09:00
parent 55265e69a4
commit 020ef8835a
Signed by: orrisroot
GPG Key ID: 470AA444C92904B2
8 changed files with 53 additions and 23 deletions

View File

@ -1,7 +1,14 @@
{ {
"version": "0.2", "version": "0.2",
"language": "en,en-gb", "language": "en,en-gb",
"ignoreWords": ["followlinks", "getframe", "pycache", "pydantic", "UNLCK"], "ignoreWords": [
"followlinks",
"getframe",
"pycache",
"pydantic",
"toolbelt",
"UNLCK"
],
"words": [ "words": [
"chacl", "chacl",
"kikan", "kikan",

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2023 Neuroinformatics Unit, RIKEN CBS Copyright (c) 2023- Neuroinformatics Unit, RIKEN CBS
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -80,6 +80,7 @@ Upload the file or directory
```shell ```shell
mdrs upload ./sample.dat neurodata:/NIU/Repository/TEST/ mdrs upload ./sample.dat neurodata:/NIU/Repository/TEST/
mdrs upload -r ./dataset neurodata:/NIU/Repository/TEST/ mdrs upload -r ./dataset neurodata:/NIU/Repository/TEST/
mdrs upload -r -s ./dataset neurodata:/NIU/Repository/TEST/
``` ```
### download ### download
@ -115,8 +116,8 @@ mdrs cp -r neurodata:/NIU/Repository/TEST/dataset neurodata:/NIU/Repository/TEST
Remove the file or folder Remove the file or folder
```shell ```shell
mdrs rm neurodata:/NIU/Repository/TEST2/sample2.dat mdrs rm neurodata:/NIU/Repository/TEST/sample.dat
mdrs rm -r neurodata:/NIU/Repository/TEST2/dataset mdrs rm -r neurodata:/NIU/Repository/TEST/dataset
``` ```
### chacl ### chacl
@ -126,7 +127,7 @@ Change the folder access level
```shell ```shell
mdrs chacl private neurodata:/NIU/Repository/Private mdrs chacl private neurodata:/NIU/Repository/Private
mdrs chacl cbs_open -r neurodata:/NIU/Repository/CBS_Open mdrs chacl cbs_open -r neurodata:/NIU/Repository/CBS_Open
mdrs chacl pw_open -r -p FOLDER_PASSWORD neurodata:/NIU/Repository/PW_Open mdrs chacl pw_open -r -p PW_OPEN_PASSWORD neurodata:/NIU/Repository/PW_Open
``` ```
### metadata ### metadata

View File

@ -1 +1 @@
1.3.1 1.3.4

View File

@ -1,8 +1,10 @@
import mimetypes
import os import os
from typing import Any, Final from typing import Any, Final
from pydantic import TypeAdapter from pydantic import TypeAdapter
from pydantic.dataclasses import dataclass from pydantic.dataclasses import dataclass
from requests_toolbelt.multipart.encoder import MultipartEncoder
from mdrsclient.api.base import BaseApi from mdrsclient.api.base import BaseApi
from mdrsclient.api.utils import token_check from mdrsclient.api.utils import token_check
@ -17,6 +19,7 @@ class FilesApiCreateResponse:
class FilesApi(BaseApi): class FilesApi(BaseApi):
ENTRYPOINT: Final[str] = "v3/files/" ENTRYPOINT: Final[str] = "v3/files/"
FALLBACK_MIMETYPE: Final[str] = "application/octet-stream"
def retrieve(self, id: str) -> File: def retrieve(self, id: str) -> File:
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name) # print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
@ -30,30 +33,42 @@ class FilesApi(BaseApi):
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name) # print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
url = self.ENTRYPOINT url = self.ENTRYPOINT
token_check(self.connection) token_check(self.connection)
data: dict[str, str | int] = {"folder_id": folder_id} data: dict[str, str | int] | MultipartEncoder = {}
try: try:
with open(os.path.realpath(path), mode="rb") as fp: with open(os.path.realpath(path), mode="rb") as fp:
response = self.connection.post(url, data=data, files={"file": fp}) data = MultipartEncoder(
fields={"folder_id": folder_id, "file": (os.path.basename(path), fp, self._get_mime_type(path))}
)
response = self.connection.post(url, data=data, headers={"Content-Type": data.content_type})
self._raise_response_error(response) self._raise_response_error(response)
ret = TypeAdapter(FilesApiCreateResponse).validate_python(response.json()) ret = TypeAdapter(FilesApiCreateResponse).validate_python(response.json())
except OSError: except OSError:
raise UnexpectedException(f"Could not open `{path}` file.") raise UnexpectedException(f"Could not open `{path}` file.")
except MemoryError:
raise UnexpectedException("Out of memory.")
except Exception as e:
print(e)
raise UnexpectedException(e)
return ret.id return ret.id
def update(self, file: File, path: str | None) -> bool: def update(self, file: File, path: str | None) -> bool:
# print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name) # print(self.__class__.__name__ + "::" + sys._getframe().f_code.co_name)
url = self.ENTRYPOINT + file.id + "/" url = self.ENTRYPOINT + file.id + "/"
token_check(self.connection) token_check(self.connection)
data: dict[str, str | int] | MultipartEncoder = {}
if path is not None: if path is not None:
# update file body # update file body
try: try:
with open(os.path.realpath(path), mode="rb") as fp: with open(os.path.realpath(path), mode="rb") as fp:
response = self.connection.put(url, files={"file": fp}) data = MultipartEncoder(fields={"file": (os.path.basename(path), fp, self._get_mime_type(path))})
response = self.connection.put(url, data=data, headers={"Content-Type": data.content_type})
except OSError: except OSError:
raise UnexpectedException(f"Could not open `{path}` file.") raise UnexpectedException(f"Could not open `{path}` file.")
except MemoryError:
raise UnexpectedException("Out of memory.")
else: else:
# update metadata # update metadata
data: dict[str, str | int] = {"name": file.name, "description": file.description} data = {"name": file.name, "description": file.description}
response = self.connection.put(url, data=data) response = self.connection.put(url, data=data)
self._raise_response_error(response) self._raise_response_error(response)
return True return True
@ -106,3 +121,9 @@ class FilesApi(BaseApi):
except PermissionError: except PermissionError:
print(f"Cannot create file `{path}`: Permission denied.") print(f"Cannot create file `{path}`: Permission denied.")
return True return True
def _get_mime_type(self, path: str) -> str:
mt = mimetypes.guess_type(path)
if mt:
return mt[0] or self.FALLBACK_MIMETYPE
return self.FALLBACK_MIMETYPE

View File

@ -106,4 +106,4 @@ class UploadCommand(BaseCommand):
file_api.update(file, info.path) file_api.update(file, info.path)
print(os.path.join(info.folder.path, basename)) print(os.path.join(info.folder.path, basename))
except MDRSException as e: except MDRSException as e:
print(f"API Error: {e}") print(f"Error: {e}")

View File

@ -1,9 +1,9 @@
import platform import platform
import threading import threading
from io import BufferedReader
from typing import TypedDict from typing import TypedDict
from requests import Response, Session from requests import Response, Session
from requests_toolbelt.multipart.encoder import MultipartEncoder
# Unpack is new in 3.11 # Unpack is new in 3.11
from typing_extensions import Unpack from typing_extensions import Unpack
@ -21,14 +21,14 @@ class _KwArgsMDRSConnectionGet(TypedDict, total=False):
class _KwArgsMDRSConnectionPost(TypedDict, total=False): class _KwArgsMDRSConnectionPost(TypedDict, total=False):
params: dict[str, str | int] params: dict[str, str | int]
data: dict[str, str | int] data: dict[str, str | int] | MultipartEncoder
files: dict[str, BufferedReader] headers: dict[str, str]
class _KwArgsMDRSConnectionPut(TypedDict, total=False): class _KwArgsMDRSConnectionPut(TypedDict, total=False):
params: dict[str, str | int] params: dict[str, str | int]
data: dict[str, str | int] data: dict[str, str | int] | MultipartEncoder
files: dict[str, BufferedReader] headers: dict[str, str]
class _KwArgsMDRSConnectionDelete(TypedDict, total=False): class _KwArgsMDRSConnectionDelete(TypedDict, total=False):

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "mdrs-client-python" name = "mdrs-client-python"
version = "1.3.3" version = "1.3.4"
description = "The mdrs-client-python is python library and a command-line client for up- and downloading files to and from MDRS based repository." description = "The mdrs-client-python is python library and a command-line client for up- and downloading files to and from MDRS based repository."
authors = ["Yoshihiro OKUMURA <yoshihiro.okumura@riken.jp>"] authors = ["Yoshihiro OKUMURA <yoshihiro.okumura@riken.jp>"]
license = "MIT" license = "MIT"
@ -22,19 +22,20 @@ packages = [
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.10" python = "^3.10"
requests = "^2.31.0" requests = "^2.32.3"
requests-toolbelt = "^1.0.0"
python-dotenv = "^1.0.1" python-dotenv = "^1.0.1"
pydantic = "^2.6.1" pydantic = "^2.8.2"
pydantic-settings = "^2.1.0" pydantic-settings = "^2.3.4"
PyJWT = "^2.8.0" PyJWT = "^2.8.0"
validators = "^0.22.0" validators = "^0.22.0"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
black = "^24.2.0" black = "^24.2.2"
flake8 = "^7.0.0" flake8 = "^7.1.0"
Flake8-pyproject = "^1.2.3" Flake8-pyproject = "^1.2.3"
isort = "^5.13.2" isort = "^5.13.2"
pyright = "^1.1.350" pyright = "^1.1.370"
[tool.poetry.scripts] [tool.poetry.scripts]
mdrs = 'mdrsclient.__main__:main' mdrs = 'mdrsclient.__main__:main'