feature(File Storage): Local File Storage
Added: - Ability to do direct download - Local File Storage (Save in django media root)
This commit is contained in:
parent
314c84403e
commit
8bd5984a94
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState } from 'react';
|
import React, {useState} from 'react';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Link } from 'react-router-dom';
|
import {Link} from 'react-router-dom';
|
||||||
|
|
||||||
function DownloadFile() {
|
function DownloadFile() {
|
||||||
const [passcode, setPasscode] = useState('');
|
const [passcode, setPasscode] = useState('');
|
||||||
|
@ -12,37 +12,42 @@ function DownloadFile() {
|
||||||
|
|
||||||
const handleDownloadFile = () => {
|
const handleDownloadFile = () => {
|
||||||
if (passcode) {
|
if (passcode) {
|
||||||
axios.get(`http://127.0.0.1:8000/api/files/${passcode}/`)
|
axios.get(`http://127.0.0.1:8000/api/files/${passcode}/`, {responseType: 'blob'})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
// print json data from response
|
let filename = 'downloaded_file'; // Default filename
|
||||||
console.log(response.data);
|
|
||||||
const fileData = response.data.file;
|
|
||||||
if (fileData) {
|
|
||||||
// Convert the data to a Blob
|
|
||||||
const blob = new Blob([fileData], { type: 'application/octet-stream' });
|
|
||||||
|
|
||||||
// Create a temporary URL for the Blob
|
// Check if the Content-Disposition header exists
|
||||||
const url = window.URL.createObjectURL(blob);
|
if (response.headers['content-disposition']) {
|
||||||
|
const contentDisposition = response.headers['content-disposition'];
|
||||||
|
const filenameMatch = contentDisposition.match(/filename="(.+)"/);
|
||||||
|
|
||||||
// Create a dynamically generated anchor element
|
if (filenameMatch) {
|
||||||
const a = document.createElement('a');
|
filename = filenameMatch[1];
|
||||||
a.href = url;
|
}
|
||||||
a.download = response.data.filename;
|
|
||||||
|
|
||||||
// Trigger a click event on the anchor element to simulate a download
|
|
||||||
a.click();
|
|
||||||
|
|
||||||
// Clean up the temporary URL
|
|
||||||
window.URL.revokeObjectURL(url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const blob = new Blob([response.data], {type: 'application/octet-stream'});
|
||||||
|
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
|
||||||
|
a.style.display = 'none';
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
|
||||||
|
// Clean up the temporary URL and remove the dynamically created anchor element
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
document.body.removeChild(a);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
// print error if any
|
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen flex flex-col items-center justify-center bg-gray-100">
|
<div className="h-screen flex flex-col items-center justify-center bg-gray-100">
|
||||||
<div className="border p-6 rounded-lg bg-white shadow-md w-96 relative">
|
<div className="border p-6 rounded-lg bg-white shadow-md w-96 relative">
|
||||||
|
@ -61,7 +66,8 @@ function DownloadFile() {
|
||||||
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring focus:border-blue-300"
|
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring focus:border-blue-300"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button onClick={handleDownloadFile} className="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded-lg w-full">
|
<button onClick={handleDownloadFile}
|
||||||
|
className="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded-lg w-full">
|
||||||
Download File
|
Download File
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -95,6 +95,10 @@ CORS_ALLOWED_ORIGINS = [
|
||||||
'http://localhost:3000',
|
'http://localhost:3000',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
CORS_EXPOSE_HEADERS = [
|
||||||
|
'Content-Disposition',
|
||||||
|
]
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
@ -177,6 +181,12 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Media Storage
|
||||||
|
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||||
|
|
||||||
|
if not os.path.exists(MEDIA_ROOT):
|
||||||
|
os.makedirs(MEDIA_ROOT)
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
import threading
|
import threading
|
||||||
import uuid
|
import uuid
|
||||||
|
import os
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from rest_framework.decorators import api_view
|
from rest_framework.decorators import api_view
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.conf import settings
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
|
||||||
@api_view(['GET', 'POST'])
|
@api_view(['POST'])
|
||||||
def manage_items(request, *args, **kwargs):
|
def manage_items(request):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
# Define a timeout value (in seconds)
|
# Define a timeout value (in seconds)
|
||||||
timeout = 5
|
timeout = 5
|
||||||
|
@ -15,6 +19,7 @@ def manage_items(request, *args, **kwargs):
|
||||||
# Get the list of files and the TTL value from the request data
|
# Get the list of files and the TTL value from the request data
|
||||||
files = request.FILES.getlist('file')
|
files = request.FILES.getlist('file')
|
||||||
ttl = request.data.get('ttl')
|
ttl = request.data.get('ttl')
|
||||||
|
|
||||||
if not ttl:
|
if not ttl:
|
||||||
# Set ttl to 3 days
|
# Set ttl to 3 days
|
||||||
ttl = 259200 # 3 * 24 * 60 * 60
|
ttl = 259200 # 3 * 24 * 60 * 60
|
||||||
|
@ -28,28 +33,33 @@ def manage_items(request, *args, **kwargs):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return Response({'msg': 'Invalid TTL format'}, status=400)
|
return Response({'msg': 'Invalid TTL format'}, status=400)
|
||||||
|
|
||||||
# Define a function to save a single file in a thread
|
# Define a function to save a single file
|
||||||
def save_file_to_redis(file):
|
def save_file_locally(file):
|
||||||
key = uuid.uuid4().hex
|
key = uuid.uuid4().hex
|
||||||
|
|
||||||
# Get the filename
|
# Get the filename
|
||||||
filename = file.name
|
filename = file.name
|
||||||
|
|
||||||
# Convert file to bytes
|
# Define the path to save the file locally
|
||||||
content = file.read()
|
save_path = os.path.join(settings.MEDIA_ROOT, filename)
|
||||||
|
|
||||||
# Set with the provided TTL
|
# Save the file locally
|
||||||
|
with open(save_path, 'wb') as destination:
|
||||||
|
for chunk in file.chunks():
|
||||||
|
destination.write(chunk)
|
||||||
|
|
||||||
|
# Store the file path in the cache with the provided TTL
|
||||||
cache.set(key,
|
cache.set(key,
|
||||||
{
|
{
|
||||||
'filename': filename,
|
'filename': filename,
|
||||||
'content': content
|
'path': save_path,
|
||||||
},
|
},
|
||||||
timeout=ttl)
|
timeout=ttl)
|
||||||
|
|
||||||
response = {
|
response = {
|
||||||
'key': key,
|
'key': key,
|
||||||
'filename': filename,
|
'filename': filename,
|
||||||
'msg': f"{key} successfully set to {filename} with TTL {ttl} seconds"
|
'msg': f"{key} successfully set to {filename} with TTL {ttl} seconds",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Append the response to the shared responses list
|
# Append the response to the shared responses list
|
||||||
|
@ -61,7 +71,7 @@ def manage_items(request, *args, **kwargs):
|
||||||
# Create a thread for each file
|
# Create a thread for each file
|
||||||
file_threads = []
|
file_threads = []
|
||||||
for file in files:
|
for file in files:
|
||||||
file_thread = threading.Thread(target=save_file_to_redis, args=(file,))
|
file_thread = threading.Thread(target=save_file_locally, args=(file,))
|
||||||
file_threads.append(file_thread)
|
file_threads.append(file_thread)
|
||||||
|
|
||||||
# Start all file-saving threads
|
# Start all file-saving threads
|
||||||
|
@ -93,62 +103,79 @@ def manage_items(request, *args, **kwargs):
|
||||||
@api_view(['GET', 'PUT', 'DELETE'])
|
@api_view(['GET', 'PUT', 'DELETE'])
|
||||||
def manage_item(request, *args, **kwargs):
|
def manage_item(request, *args, **kwargs):
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
if kwargs['key']:
|
if 'key' in kwargs:
|
||||||
value = cache.get(kwargs['key'])
|
value = cache.get(kwargs['key'])
|
||||||
if value:
|
if value:
|
||||||
response = {
|
# Check if the 'path' key is in the stored value
|
||||||
'key': kwargs['key'],
|
if 'path' in value:
|
||||||
'file': value['content'],
|
file_path = value['path']
|
||||||
'filename': value['filename'],
|
if os.path.exists(file_path):
|
||||||
'msg': 'success'
|
with open(file_path, 'rb') as f:
|
||||||
}
|
response = HttpResponse(f.read(), content_type='application/octet-stream')
|
||||||
return Response(response, status=200)
|
response['Content-Disposition'] = f'attachment; filename="{quote(value["filename"])}"'
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
response = {
|
||||||
|
'msg': 'File not found'
|
||||||
|
}
|
||||||
|
return Response(response, status=404)
|
||||||
else:
|
else:
|
||||||
response = {
|
response = {
|
||||||
'key': kwargs['key'],
|
|
||||||
'file': None,
|
|
||||||
'filename': None,
|
|
||||||
'msg': 'Not found'
|
'msg': 'Not found'
|
||||||
}
|
}
|
||||||
return Response(response, status=404)
|
return Response(response, status=404)
|
||||||
|
|
||||||
# elif request.method == 'PUT':
|
|
||||||
# if kwargs['key']:
|
|
||||||
# request_data = json.loads(request.body)
|
|
||||||
# new_value = request_data['value']
|
|
||||||
# value = redis_instance.get(kwargs['key'])
|
|
||||||
# if value:
|
|
||||||
# redis_instance.set(kwargs['key'], new_value)
|
|
||||||
# response = {
|
|
||||||
# 'key': kwargs['key'],
|
|
||||||
# 'file': value,
|
|
||||||
# 'msg': f"Successfully updated {kwargs['key']}"
|
|
||||||
# }
|
|
||||||
# return Response(response, status=200)
|
|
||||||
# else:
|
|
||||||
# response = {
|
|
||||||
# 'key': kwargs['key'],
|
|
||||||
# 'value': None,
|
|
||||||
# 'msg': 'Not found'
|
|
||||||
# }
|
|
||||||
# return Response(response, status=404)
|
|
||||||
|
|
||||||
elif request.method == 'DELETE':
|
elif request.method == 'DELETE':
|
||||||
if kwargs['key']:
|
if 'key' in kwargs:
|
||||||
result = cache.delete(kwargs['key'])
|
value = cache.get(kwargs['key'])
|
||||||
if result == 1:
|
if value:
|
||||||
response = {
|
if 'path' in value:
|
||||||
'msg': f"{kwargs['key']} successfully deleted"
|
file_path = value['path']
|
||||||
}
|
|
||||||
return Response(response, status=404)
|
# Check if the file exists
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
# Delete the file
|
||||||
|
os.remove(file_path)
|
||||||
|
|
||||||
|
# Delete the cache entry
|
||||||
|
cache.delete(kwargs['key'])
|
||||||
|
|
||||||
|
response = {
|
||||||
|
'msg': f"{kwargs['key']} successfully deleted"
|
||||||
|
}
|
||||||
|
return Response(response, status=200)
|
||||||
|
else:
|
||||||
|
response = {
|
||||||
|
'msg': 'File not found'
|
||||||
|
}
|
||||||
|
return Response(response, status=404)
|
||||||
else:
|
else:
|
||||||
response = {
|
response = {
|
||||||
'key': kwargs['key'],
|
'key': kwargs['key'],
|
||||||
'file': None,
|
|
||||||
'msg': 'Not found'
|
'msg': 'Not found'
|
||||||
}
|
}
|
||||||
return Response(response, status=404)
|
return Response(response, status=404)
|
||||||
|
|
||||||
|
# elif request.method == 'PUT':
|
||||||
|
# if kwargs['key']:
|
||||||
|
# request_data = json.loads(request.body)
|
||||||
|
# new_value = request_data['value']
|
||||||
|
# value = redis_instance.get(kwargs['key'])
|
||||||
|
# if value:
|
||||||
|
# redis_instance.set(kwargs['key'], new_value)
|
||||||
|
# response = {
|
||||||
|
# 'key': kwargs['key'],
|
||||||
|
# 'file': value,
|
||||||
|
# 'msg': f"Successfully updated {kwargs['key']}"
|
||||||
|
# }
|
||||||
|
# return Response(response, status=200)
|
||||||
|
# else:
|
||||||
|
# response = {
|
||||||
|
# 'key': kwargs['key'],
|
||||||
|
# 'value': None,
|
||||||
|
# 'msg': 'Not found'
|
||||||
|
# }
|
||||||
|
# return Response(response, status=404)
|
||||||
# class FileView(viewsets.ModelViewSet):
|
# class FileView(viewsets.ModelViewSet):
|
||||||
# queryset = File.objects.all()
|
# queryset = File.objects.all()
|
||||||
# serializer_class = FileSerializer
|
# serializer_class = FileSerializer
|
||||||
|
|
Loading…
Reference in New Issue