better file uploading support
parent
fc5bb08a0d
commit
815707b8ad
|
|
@ -2,8 +2,10 @@ using System;
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
|
@ -14,7 +16,8 @@ namespace VELConnect
|
|||
public class VELConnectManager : MonoBehaviour
|
||||
{
|
||||
public string velConnectUrl = "http://localhost";
|
||||
private static VELConnectManager instance;
|
||||
public static string VelConnectUrl => _instance.velConnectUrl;
|
||||
private static VELConnectManager _instance;
|
||||
|
||||
public class State
|
||||
{
|
||||
|
|
@ -27,6 +30,7 @@ namespace VELConnect
|
|||
public string last_modified;
|
||||
public Dictionary<string, string> data;
|
||||
}
|
||||
|
||||
public class Device
|
||||
{
|
||||
public string hw_id;
|
||||
|
|
@ -136,8 +140,8 @@ namespace VELConnect
|
|||
|
||||
private void Awake()
|
||||
{
|
||||
if (instance != null) Debug.LogError("VELConnectManager instance already exists", this);
|
||||
instance = this;
|
||||
if (_instance != null) Debug.LogError("VELConnectManager instance already exists", this);
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
// Start is called before the first frame update
|
||||
|
|
@ -382,10 +386,10 @@ namespace VELConnect
|
|||
|
||||
if (sendInitialState)
|
||||
{
|
||||
if (instance != null && instance.lastState?.device != null)
|
||||
if (_instance != null && _instance.lastState?.device != null)
|
||||
{
|
||||
if (instance.lastState.device.GetType().GetField(key)
|
||||
?.GetValue(instance.lastState.device) is string val)
|
||||
if (_instance.lastState.device.GetType().GetField(key)
|
||||
?.GetValue(_instance.lastState.device) is string val)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -472,12 +476,12 @@ namespace VELConnect
|
|||
|
||||
public static string GetDeviceData(string key)
|
||||
{
|
||||
return instance != null ? instance.lastState?.device?.TryGetData(key) : null;
|
||||
return _instance != null ? _instance.lastState?.device?.TryGetData(key) : null;
|
||||
}
|
||||
|
||||
public static string GetRoomData(string key)
|
||||
{
|
||||
return instance != null ? instance.lastState?.room?.TryGetData(key) : null;
|
||||
return _instance != null ? _instance.lastState?.room?.TryGetData(key) : null;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -486,8 +490,8 @@ namespace VELConnect
|
|||
/// </summary>
|
||||
public static void SetDeviceField(Dictionary<string, object> device)
|
||||
{
|
||||
instance.PostRequestCallback(
|
||||
instance.velConnectUrl + "/api/device/set_data/" + DeviceId,
|
||||
PostRequestCallback(
|
||||
_instance.velConnectUrl + "/api/device/set_data/" + DeviceId,
|
||||
JsonConvert.SerializeObject(device),
|
||||
new Dictionary<string, string> { { "modified_by", DeviceId } }
|
||||
);
|
||||
|
|
@ -498,8 +502,8 @@ namespace VELConnect
|
|||
/// </summary>
|
||||
public static void SetDeviceData(Dictionary<string, string> data)
|
||||
{
|
||||
instance.PostRequestCallback(
|
||||
instance.velConnectUrl + "/api/device/set_data/" + DeviceId,
|
||||
PostRequestCallback(
|
||||
_instance.velConnectUrl + "/api/device/set_data/" + DeviceId,
|
||||
JsonConvert.SerializeObject(new Dictionary<string, object> { { "data", data } }),
|
||||
new Dictionary<string, string> { { "modified_by", DeviceId } }
|
||||
);
|
||||
|
|
@ -513,18 +517,34 @@ namespace VELConnect
|
|||
return;
|
||||
}
|
||||
|
||||
instance.PostRequestCallback(
|
||||
instance.velConnectUrl + "/api/set_data/" + Application.productName + "_" + VelNetManager.Room,
|
||||
PostRequestCallback(
|
||||
_instance.velConnectUrl + "/api/set_data/" + Application.productName + "_" + VelNetManager.Room,
|
||||
JsonConvert.SerializeObject(data),
|
||||
new Dictionary<string, string> { { "modified_by", DeviceId } }
|
||||
);
|
||||
}
|
||||
|
||||
public static void UploadFile(string fileName, byte[] fileData, Action<string> successCallback = null)
|
||||
{
|
||||
MultipartFormDataContent requestContent = new MultipartFormDataContent();
|
||||
ByteArrayContent fileContent = new ByteArrayContent(fileData);
|
||||
|
||||
public void GetRequestCallback(string url, Action<string> successCallback = null,
|
||||
requestContent.Add(fileContent, "file", fileName);
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
HttpResponseMessage r = await new HttpClient().PostAsync(_instance.velConnectUrl + "/api/upload_file", requestContent);
|
||||
string resp = await r.Content.ReadAsStringAsync();
|
||||
Dictionary<string, string> dict = JsonConvert.DeserializeObject<Dictionary<string,string>>(resp);
|
||||
successCallback?.Invoke(dict["key"]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static void GetRequestCallback(string url, Action<string> successCallback = null,
|
||||
Action<string> failureCallback = null)
|
||||
{
|
||||
StartCoroutine(GetRequestCallbackCo(url, successCallback, failureCallback));
|
||||
_instance.StartCoroutine(_instance.GetRequestCallbackCo(url, successCallback, failureCallback));
|
||||
}
|
||||
|
||||
private IEnumerator GetRequestCallbackCo(string url, Action<string> successCallback = null,
|
||||
|
|
@ -548,13 +568,14 @@ namespace VELConnect
|
|||
}
|
||||
}
|
||||
|
||||
public void PostRequestCallback(string url, string postData, Dictionary<string, string> headers = null,
|
||||
public static void PostRequestCallback(string url, string postData, Dictionary<string, string> headers = null,
|
||||
Action<string> successCallback = null,
|
||||
Action<string> failureCallback = null)
|
||||
{
|
||||
StartCoroutine(PostRequestCallbackCo(url, postData, headers, successCallback, failureCallback));
|
||||
_instance.StartCoroutine(PostRequestCallbackCo(url, postData, headers, successCallback, failureCallback));
|
||||
}
|
||||
|
||||
|
||||
private static IEnumerator PostRequestCallbackCo(string url, string postData,
|
||||
Dictionary<string, string> headers = null, Action<string> successCallback = null,
|
||||
Action<string> failureCallback = null)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ CREATE TABLE `DataBlock` (
|
|||
`id` TEXT NOT NULL,
|
||||
-- id of the owner of this file. Ownership is not transferable because ids may collide,
|
||||
-- but the owner could be null for global scope
|
||||
`owner_id` TEXT,
|
||||
`owner_id` TEXT NOT NULL DEFAULT 'none',
|
||||
`visibility` TEXT CHECK( `visibility` IN ('public','private','unlisted') ) NOT NULL DEFAULT 'public',
|
||||
-- This is an indexable field to filter out different types of datablocks
|
||||
`category` TEXT,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import os
|
||||
import secrets
|
||||
import json
|
||||
import string
|
||||
|
|
@ -129,7 +130,7 @@ def create_device(hw_id: str):
|
|||
|
||||
|
||||
@router.get('/device/get_data/{hw_id}')
|
||||
def get_state(request: Request, response: Response, hw_id: str):
|
||||
def get_device_data(request: Request, response: Response, hw_id: str):
|
||||
"""Gets the device state"""
|
||||
|
||||
devices = db.query("""
|
||||
|
|
@ -155,7 +156,7 @@ def get_state(request: Request, response: Response, hw_id: str):
|
|||
|
||||
|
||||
@router.post('/device/set_data/{hw_id}')
|
||||
def set_state(request: fastapi.Request, hw_id: str, data: dict, modified_by: str = None):
|
||||
def set_device_data(request: fastapi.Request, hw_id: str, data: dict, modified_by: str = None):
|
||||
"""Sets the device state"""
|
||||
|
||||
create_device(hw_id)
|
||||
|
|
@ -343,21 +344,53 @@ def get_user_dict(user_id: str) -> dict | None:
|
|||
return None
|
||||
|
||||
|
||||
@router.post("/upload_file")
|
||||
async def upload_file_with_random_key(request: fastapi.Request, file: UploadFile, modified_by: str = None):
|
||||
return await upload_file(request, file, None, modified_by)
|
||||
|
||||
|
||||
@router.post("/upload_file/{key}")
|
||||
async def upload_file(request: fastapi.Request, file: UploadFile, key: str, modified_by: str = None):
|
||||
async def upload_file(request: fastapi.Request, file: UploadFile, key: str | None, modified_by: str = None):
|
||||
if not os.path.exists('data'):
|
||||
os.makedirs('data')
|
||||
|
||||
# generates a key if none was supplied
|
||||
if key is None:
|
||||
key = generate_id()
|
||||
|
||||
# regenerate if necessary
|
||||
while len(db.query("SELECT id FROM `DataBlock` WHERE id=:id;", {"id": key})) > 0:
|
||||
key = generate_id()
|
||||
|
||||
async with aiofiles.open('data/' + key, 'wb') as out_file:
|
||||
content = await file.read() # async read
|
||||
await out_file.write(content) # async write
|
||||
# add a datablock to link to the file
|
||||
set_data(request, {'filename': file.filename}, key, 'file', modified_by)
|
||||
return {"filename": file.filename}
|
||||
set_data(request, data={'filename': file.filename}, key=key, category='file', modified_by=modified_by)
|
||||
return {"filename": file.filename, 'key': key}
|
||||
|
||||
|
||||
@router.get("/download_file/{key}")
|
||||
async def download_file(key: str):
|
||||
async def download_file(response: Response, key: str):
|
||||
# get the relevant datablock
|
||||
data = get_data(key)
|
||||
data = get_data(response, key)
|
||||
print(data)
|
||||
if response.status_code == status.HTTP_404_NOT_FOUND:
|
||||
return 'Not found'
|
||||
if data['category'] != 'file':
|
||||
return 'Not a file', 500
|
||||
return fastapi.FileResponse(data['data']['filename'])
|
||||
response.status_code = status.HTTP_400_BAD_REQUEST
|
||||
return 'Not a file'
|
||||
return FileResponse(path='data/' + key, filename=data['data']['filename'])
|
||||
|
||||
|
||||
@router.get("/get_all_files")
|
||||
async def get_all_files():
|
||||
data = db.query("""
|
||||
SELECT *
|
||||
FROM `DataBlock`
|
||||
WHERE visibility='public' AND category='file';
|
||||
""")
|
||||
data = [dict(f) for f in data]
|
||||
for f in data:
|
||||
parse_data(f)
|
||||
return data
|
||||
|
|
|
|||
Loading…
Reference in New Issue