mirror of
https://github.com/PeernetOfficial/core.git
synced 2026-07-17 02:47:51 +01:00
webapi: New function /warehouse/read/path to read a file from warehouse to disk
This commit is contained in:
@@ -29,6 +29,8 @@ const (
|
||||
StatusErrorDeleteFile = 10 // Error deleting file.
|
||||
StatusErrorReadFile = 11 // Error reading file.
|
||||
StatusErrorSeekFile = 12 // Error seeking to position in file.
|
||||
StatusErrorTargetExists = 13 // Target file already exists.
|
||||
StatusErrorCreateTarget = 14 // Error creating target file.
|
||||
)
|
||||
|
||||
// CreateFile creates a new file in the warehouse
|
||||
@@ -115,6 +117,7 @@ func (wh *Warehouse) CreateFileFromPath(file string) (hash []byte, status int, e
|
||||
|
||||
// ReadFile reads a file from the warehouse and outputs it to the writer
|
||||
// Offset is the position in the file to start reading. Limit (0 = not used) defines how many bytes to read starting at the offset.
|
||||
// Return status codes: StatusInvalidHash, StatusFileNotFound, StatusErrorOpenFile, StatusErrorSeekFile, StatusErrorReadFile, StatusOK
|
||||
func (wh *Warehouse) ReadFile(hash []byte, offset, limit int64, writer io.Writer) (status int, bytesRead int64, err error) {
|
||||
path, _, status, err := wh.FileExists(hash)
|
||||
if status != StatusOK { // file does not exist or invalid hash
|
||||
@@ -178,7 +181,7 @@ func (wh *Warehouse) DeleteFile(hash []byte) (status int, err error) {
|
||||
return StatusOK, nil
|
||||
}
|
||||
|
||||
// FileExists checks if the file exists
|
||||
// FileExists checks if the file exists. It returns StatusInvalidHash, StatusFileNotFound, or StatusOK.
|
||||
func (wh *Warehouse) FileExists(hash []byte) (path string, fileInfo os.FileInfo, status int, err error) {
|
||||
hashA, err := ValidateHash(hash)
|
||||
if err != nil {
|
||||
@@ -204,3 +207,22 @@ func (wh *Warehouse) DeleteWarehouse() (err error) {
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// ReadFileToDisk reads a file from the warehouse and outputs it to the target file. The function fails with StatusErrorTargetExists if the target file already exists.
|
||||
// Offset is the position in the file to start reading. Limit (0 = not used) defines how many bytes to read starting at the offset.
|
||||
// Return status codes: StatusInvalidHash, StatusFileNotFound, StatusErrorTargetExists, StatusErrorCreateTarget, StatusErrorOpenFile, StatusErrorSeekFile, StatusErrorReadFile, StatusOK
|
||||
func (wh *Warehouse) ReadFileToDisk(hash []byte, offset, limit int64, fileTarget string) (status int, bytesRead int64, err error) {
|
||||
// check if the target file already exist
|
||||
if _, err := os.Stat(fileTarget); err == nil {
|
||||
return StatusErrorTargetExists, 0, nil
|
||||
}
|
||||
|
||||
// create the target file
|
||||
fileT, err := os.OpenFile(fileTarget, os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return StatusErrorCreateTarget, 0, err
|
||||
}
|
||||
defer fileT.Close()
|
||||
|
||||
return wh.ReadFile(hash, offset, limit, fileT)
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ func Start(ListenAddresses []string, UseSSL bool, CertificateFile, CertificateKe
|
||||
Router.HandleFunc("/warehouse/create", apiWarehouseCreateFile).Methods("POST")
|
||||
Router.HandleFunc("/warehouse/create/path", apiWarehouseCreateFilePath).Methods("GET")
|
||||
Router.HandleFunc("/warehouse/read", apiWarehouseReadFile).Methods("GET")
|
||||
Router.HandleFunc("/warehouse/read/path", apiWarehouseReadFilePath).Methods("GET")
|
||||
Router.HandleFunc("/warehouse/delete", apiWarehouseDeleteFile).Methods("GET")
|
||||
Router.HandleFunc("/file/read", apiFileRead).Methods("GET")
|
||||
Router.HandleFunc("/file/view", apiFileView).Methods("GET")
|
||||
|
||||
@@ -30,7 +30,7 @@ func apiWarehouseCreateFile(w http.ResponseWriter, r *http.Request) {
|
||||
hash, status, err := core.UserWarehouse.CreateFile(r.Body)
|
||||
|
||||
if err != nil {
|
||||
core.Filters.LogError("warehouese.CreateFile", "status %d error: %v", status, err)
|
||||
core.Filters.LogError("warehouse.CreateFile", "status %d error: %v", status, err)
|
||||
}
|
||||
|
||||
EncodeJSON(w, r, WarehouseResult{Status: status, Hash: hash})
|
||||
@@ -55,7 +55,7 @@ func apiWarehouseCreateFilePath(w http.ResponseWriter, r *http.Request) {
|
||||
hash, status, err := core.UserWarehouse.CreateFileFromPath(filePath)
|
||||
|
||||
if err != nil {
|
||||
core.Filters.LogError("warehouese.CreateFile", "status %d error: %v", status, err)
|
||||
core.Filters.LogError("warehouse.CreateFile", "status %d error: %v", status, err)
|
||||
}
|
||||
|
||||
EncodeJSON(w, r, WarehouseResult{Status: status, Hash: hash})
|
||||
@@ -90,12 +90,12 @@ func apiWarehouseReadFile(w http.ResponseWriter, r *http.Request) {
|
||||
case warehouse.StatusInvalidHash, warehouse.StatusErrorOpenFile, warehouse.StatusErrorSeekFile:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
// Cannot catch WarehouseStatusErrorReadFile since data may have been already returned.
|
||||
// Cannot catch warehouse.StatusErrorReadFile since data may have been already returned.
|
||||
// In the future a special header indicating the expected file length could be sent (would require a callback in ReadFile), although the caller should already know the file size based on metadata.
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
core.Filters.LogError("warehouese.ReadFile", "status %d read %d error: %v", status, bytesRead, err)
|
||||
core.Filters.LogError("warehouse.ReadFile", "status %d read %d error: %v", status, bytesRead, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,36 @@ func apiWarehouseDeleteFile(w http.ResponseWriter, r *http.Request) {
|
||||
status, err := core.UserWarehouse.DeleteFile(hash)
|
||||
|
||||
if err != nil {
|
||||
core.Filters.LogError("warehouese.DeleteFile", "status %d error: %v", status, err)
|
||||
core.Filters.LogError("warehouse.DeleteFile", "status %d error: %v", status, err)
|
||||
}
|
||||
|
||||
EncodeJSON(w, r, WarehouseResult{Status: status, Hash: hash})
|
||||
}
|
||||
|
||||
/*
|
||||
apiWarehouseReadFilePath reads a file from the warehouse and stores it to the target file. It fails with StatusErrorTargetExists if the target file already exists.
|
||||
The path must include the full directory and file name.
|
||||
|
||||
Request: GET /warehouse/read/path?hash=[hash]&path=[target path on disk]
|
||||
Optional parameters &offset=[file offset]&limit=[read limit in bytes]
|
||||
Response: 200 with JSON structure WarehouseResult
|
||||
*/
|
||||
func apiWarehouseReadFilePath(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
hash, valid1 := DecodeBlake3Hash(r.Form.Get("hash"))
|
||||
if !valid1 {
|
||||
http.Error(w, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
targetFile := r.Form.Get("path")
|
||||
offset, _ := strconv.Atoi(r.Form.Get("offset"))
|
||||
limit, _ := strconv.Atoi(r.Form.Get("limit"))
|
||||
|
||||
status, bytesRead, err := core.UserWarehouse.ReadFileToDisk(hash, int64(offset), int64(limit), targetFile)
|
||||
|
||||
if err != nil {
|
||||
core.Filters.LogError("warehouse.ReadFileToDisk", "status %d read %d error: %v", status, bytesRead, err)
|
||||
}
|
||||
|
||||
EncodeJSON(w, r, WarehouseResult{Status: status, Hash: hash})
|
||||
|
||||
135
webapi/readme.md
135
webapi/readme.md
@@ -70,6 +70,7 @@ These are the functions provided by the API:
|
||||
/warehouse/create Create a file in the warehouse
|
||||
/warehouse/create/path Create a file in the warehouse via copy
|
||||
/warehouse/read Read a file in the warehouse
|
||||
/warehouse/read/path Read a file in the warehouse to disk
|
||||
/warehouse/delete Delete a file in the warehouse
|
||||
```
|
||||
|
||||
@@ -135,14 +136,14 @@ Result: 204 if the user choses not to delete the account
|
||||
|
||||
Common status codes returned by various endpoints in the `blockchain` package:
|
||||
|
||||
| Status | Constant | Info |
|
||||
|------|----------------|-------------------------------|
|
||||
| 0 | StatusOK | Successful operation. |
|
||||
| 1 | StatusBlockNotFound | Missing block in the blockchain. |
|
||||
| 2 | StatusCorruptBlock | Error block encoding. |
|
||||
| 3 | StatusCorruptBlockRecord | Error block record encoding. |
|
||||
| 4 | StatusDataNotFound | Requested data not available in the blockchain. |
|
||||
| 5 | StatusNotInWarehouse | File to be added to blockchain does not exist in the Warehouse. |
|
||||
| Status | Constant | Info |
|
||||
| ------ | ------------------------ | --------------------------------------------------------------- |
|
||||
| 0 | StatusOK | Successful operation. |
|
||||
| 1 | StatusBlockNotFound | Missing block in the blockchain. |
|
||||
| 2 | StatusCorruptBlock | Error block encoding. |
|
||||
| 3 | StatusCorruptBlockRecord | Error block record encoding. |
|
||||
| 4 | StatusDataNotFound | Requested data not available in the blockchain. |
|
||||
| 5 | StatusNotInWarehouse | File to be added to blockchain does not exist in the Warehouse. |
|
||||
|
||||
### Blockchain Header
|
||||
|
||||
@@ -245,15 +246,15 @@ type apiFileMetadata struct {
|
||||
|
||||
Below is the list of defined metadata types. Undefined types may be used by clients, but are always mapped into the `blob` field. Virtual tags are generated at runtime and are read-only. They cannot be stored on the blockchain.
|
||||
|
||||
| Type | Constant | Encoding | Virtual | Info |
|
||||
|------|----------------|----------|---------|------------------------------------------------------------------------------------------------------------------------|
|
||||
| 0 | TagName | Text | | Mapped into Name field. Name of file. |
|
||||
| 1 | TagFolder | Text | | Mapped into Folder field. Folder name. |
|
||||
| 2 | TagDescription | Text | | Mapped into Description field. Arbitrary description of the file. May contain hashtags. |
|
||||
| 3 | TagDateShared | Date | x | Mapped into Date field. When the file was published on the blockchain. |
|
||||
| 4 | TagDateCreated | Date | | Date when the file was originally created. |
|
||||
| 5 | TagSharedByCount | Number | x | Count of peers that share the file. |
|
||||
| 6 | TagSharedByGeoIP | Text/CSV | x | GeoIP data of peers that are sharing the file. CSV encoded with header "latitude,longitude". |
|
||||
| Type | Constant | Encoding | Virtual | Info |
|
||||
| ---- | ---------------- | -------- | ------- | -------------------------------------------------------------------------------------------- |
|
||||
| 0 | TagName | Text | | Mapped into Name field. Name of file. |
|
||||
| 1 | TagFolder | Text | | Mapped into Folder field. Folder name. |
|
||||
| 2 | TagDescription | Text | | Mapped into Description field. Arbitrary description of the file. May contain hashtags. |
|
||||
| 3 | TagDateShared | Date | x | Mapped into Date field. When the file was published on the blockchain. |
|
||||
| 4 | TagDateCreated | Date | | Date when the file was originally created. |
|
||||
| 5 | TagSharedByCount | Number | x | Count of peers that share the file. |
|
||||
| 6 | TagSharedByGeoIP | Text/CSV | x | GeoIP data of peers that are sharing the file. CSV encoded with header "latitude,longitude". |
|
||||
|
||||
### Add File
|
||||
|
||||
@@ -422,7 +423,7 @@ Note that all profile data is arbitrary and shall be considered untrusted and un
|
||||
Below is the list of well known profile information. Clients may define additional fields. The purpose of this defined list is to provide a common mapping across different client software. Undefined types are always mapped into the `blob` field.
|
||||
|
||||
| Type | Constant | Encoding | Info |
|
||||
|------|----------------|----------|-------------------------------|
|
||||
| ---- | -------------- | -------- | ----------------------------- |
|
||||
| 0 | ProfileName | Text | Arbitrary username |
|
||||
| 1 | ProfileEmail | Text | Email address |
|
||||
| 2 | ProfileWebsite | Text | Website address |
|
||||
@@ -556,19 +557,19 @@ Filters and sort order may be applied when starting the search at `/search`, or
|
||||
|
||||
These are the available sort options:
|
||||
|
||||
| Sort | Constant | Info |
|
||||
|------|----------------|-------------------------------|
|
||||
| 0 | SortNone | No sorting. Results are returned as they come in. |
|
||||
| 1 | SortRelevanceAsc | Least relevant results first. |
|
||||
| 2 | SortRelevanceDec | Most relevant results first. |
|
||||
| 3 | SortDateAsc | Oldest first. |
|
||||
| 4 | SortDateDesc | Newest first. |
|
||||
| 5 | SortNameAsc | File name ascending. The folder name is not used for sorting. |
|
||||
| 6 | SortNameDesc | File name descending. The folder name is not used for sorting. |
|
||||
| 7 | SortSizeAsc | File size ascending. Smallest files first. |
|
||||
| 8 | SortSizeDesc | File size descending. Largest files first. |
|
||||
| 9 | SortSharedByCountAsc | Shared by count ascending. Files that are shared by the least count of peers first. |
|
||||
| 10 | SortSharedByCountDesc | Shared by count descending. Files that are shared by the most count of peers first. |
|
||||
| Sort | Constant | Info |
|
||||
| ---- | --------------------- | ----------------------------------------------------------------------------------- |
|
||||
| 0 | SortNone | No sorting. Results are returned as they come in. |
|
||||
| 1 | SortRelevanceAsc | Least relevant results first. |
|
||||
| 2 | SortRelevanceDec | Most relevant results first. |
|
||||
| 3 | SortDateAsc | Oldest first. |
|
||||
| 4 | SortDateDesc | Newest first. |
|
||||
| 5 | SortNameAsc | File name ascending. The folder name is not used for sorting. |
|
||||
| 6 | SortNameDesc | File name descending. The folder name is not used for sorting. |
|
||||
| 7 | SortSizeAsc | File size ascending. Smallest files first. |
|
||||
| 8 | SortSizeDesc | File size descending. Largest files first. |
|
||||
| 9 | SortSharedByCountAsc | Shared by count ascending. Files that are shared by the least count of peers first. |
|
||||
| 10 | SortSharedByCountDesc | Shared by count descending. Files that are shared by the most count of peers first. |
|
||||
|
||||
The following filters are supported:
|
||||
|
||||
@@ -759,24 +760,24 @@ Response: 204 Empty
|
||||
|
||||
Downloads can have these status types:
|
||||
|
||||
| Status | Constant | Info |
|
||||
|------|----------------|-------------------------------|
|
||||
| 0 | DownloadWaitMetadata | Wait for file metadata. |
|
||||
| 1 | DownloadWaitSwarm | Wait to join swarm. |
|
||||
| 2 | DownloadActive | Active downloading. It could still be stuck at any percentage (including 0%) if no seeders are available. |
|
||||
| 3 | DownloadPause | Paused by the user. |
|
||||
| 4 | DownloadCanceled | Canceled by the user before the download finished. Once canceled, a new download has to be started if the file shall be downloaded. |
|
||||
| 5 | DownloadFinished | Download finished 100%. |
|
||||
| Status | Constant | Info |
|
||||
| ------ | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 0 | DownloadWaitMetadata | Wait for file metadata. |
|
||||
| 1 | DownloadWaitSwarm | Wait to join swarm. |
|
||||
| 2 | DownloadActive | Active downloading. It could still be stuck at any percentage (including 0%) if no seeders are available. |
|
||||
| 3 | DownloadPause | Paused by the user. |
|
||||
| 4 | DownloadCanceled | Canceled by the user before the download finished. Once canceled, a new download has to be started if the file shall be downloaded. |
|
||||
| 5 | DownloadFinished | Download finished 100%. |
|
||||
|
||||
The API response codes for download functions are:
|
||||
|
||||
| Status | Constant | Info |
|
||||
|------|----------------|-------------------------------|
|
||||
| 0 | DownloadResponseSuccess | Success |
|
||||
| 1 | DownloadResponseIDNotFound | Error: Download ID not found. |
|
||||
| 2 | DownloadResponseFileInvalid | Error: Target file cannot be used. For example, permissions denied to create it. |
|
||||
| 3 | DownloadResponseActionInvalid | Error: Invalid action. Pausing a non-active download, resuming a non-paused download, or canceling already canceled or finished download. |
|
||||
| 4 | DownloadResponseFileWrite | Error writing file. |
|
||||
| Status | Constant | Info |
|
||||
| ------ | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 0 | DownloadResponseSuccess | Success |
|
||||
| 1 | DownloadResponseIDNotFound | Error: Download ID not found. |
|
||||
| 2 | DownloadResponseFileInvalid | Error: Target file cannot be used. For example, permissions denied to create it. |
|
||||
| 3 | DownloadResponseActionInvalid | Error: Invalid action. Pausing a non-active download, resuming a non-paused download, or canceling already canceled or finished download. |
|
||||
| 4 | DownloadResponseFileWrite | Error writing file. |
|
||||
|
||||
### Start Download
|
||||
|
||||
@@ -929,20 +930,21 @@ Note: The Warehouse does NOT store files downloaded from other users. It strictl
|
||||
|
||||
Status codes:
|
||||
|
||||
| Status | Constant | Info |
|
||||
|------|----------------|-------------------------------|
|
||||
| 0 | StatusOK | Success |
|
||||
| 1 | StatusErrorCreateTempFile | Error creating a temporary file. |
|
||||
| 2 | StatusErrorWriteTempFile | Error writing temporary file. |
|
||||
| 3 | StatusErrorCloseTempFile | Error closing temporary file. |
|
||||
| 4 | StatusErrorRenameTempFile | Error renaming temporary file. |
|
||||
| 5 | StatusErrorCreatePath | Error creating path for target file in warehouse. |
|
||||
| 7 | StatusErrorOpenFile | Error opening file. |
|
||||
| 8 | StatusInvalidHash | Invalid hash. |
|
||||
| 9 | StatusFileNotFound | File not found. |
|
||||
| 10 | StatusErrorDeleteFile | Error deleting file. |
|
||||
| 11 | StatusErrorReadFile | Error reading file. |
|
||||
| 12 | StatusErrorSeekFile | Error seeking to position in file. |
|
||||
| Status | Constant | Info |
|
||||
| ------ | ------------------------- | ------------------------------------------------- |
|
||||
| 0 | StatusOK | Success |
|
||||
| 1 | StatusErrorCreateTempFile | Error creating a temporary file. |
|
||||
| 2 | StatusErrorWriteTempFile | Error writing temporary file. |
|
||||
| 3 | StatusErrorCloseTempFile | Error closing temporary file. |
|
||||
| 4 | StatusErrorRenameTempFile | Error renaming temporary file. |
|
||||
| 5 | StatusErrorCreatePath | Error creating path for target file in warehouse. |
|
||||
| 7 | StatusErrorOpenFile | Error opening file. |
|
||||
| 8 | StatusInvalidHash | Invalid hash. |
|
||||
| 9 | StatusFileNotFound | File not found. |
|
||||
| 10 | StatusErrorDeleteFile | Error deleting file. |
|
||||
| 11 | StatusErrorReadFile | Error reading file. |
|
||||
| 12 | StatusErrorSeekFile | Error seeking to position in file. |
|
||||
| 13 | StatusErrorTargetExists | Target file already exists. |
|
||||
|
||||
### Create File
|
||||
|
||||
@@ -1012,6 +1014,19 @@ Response: 200 with the raw file data
|
||||
|
||||
Example request: `http://127.0.0.1:112/warehouse/read?hash=dbf344f23e7820261329883ae26f64929a7c9977549001d28dc40c9202d7651e`
|
||||
|
||||
### Read File To Disk
|
||||
|
||||
This reads a file from the warehouse and stores it to the target file. It fails with StatusErrorTargetExists if the target file already exists.
|
||||
The path must include the full directory and file name.
|
||||
|
||||
```
|
||||
Request: GET /warehouse/read/path?hash=[hash]&path=[target path on disk]
|
||||
Optional parameters &offset=[file offset]&limit=[read limit in bytes]
|
||||
Response: 200 with JSON structure WarehouseResult
|
||||
```
|
||||
|
||||
Example request: `http://127.0.0.1:112/warehouse/read/path?hash=dbf344f23e7820261329883ae26f64929a7c9977549001d28dc40c9202d7651e&path=C%3A%5CTest%20File%202.bin`
|
||||
|
||||
### Delete File
|
||||
|
||||
This deletes a file in the warehouse. This is normally not needed, since `/blockchain/file/delete` will automatically delete files in the Warehouse if there are no active references.
|
||||
|
||||
Reference in New Issue
Block a user