better file uploading support

dev
NtsFranz 2022-09-11 01:04:03 -04:00
parent fc5bb08a0d
commit 815707b8ad
3 changed files with 82 additions and 28 deletions

View File

@ -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)

View File

@ -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,

View File

@ -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