I am writing a file upload using Torando. My API will receive only one excel file and save it in a folder. However, right now, I keep getting file corrupted error.
Here's the basic code of the API
def post(self):
user = {
"name": "Test"
}
if user:
if "test_file" not in self.request.files:
return self.write(json_util.dumps({
"message": "Required excel file missing.",
}))
bulk_excel = self.request.files['test_file'][0]
original_fname = bulk_excel['filename']
extension = os.path.splitext(original_fname)[1]
fname = ''.join(random.choice(
string.ascii_lowercase + string.digits) for _ in range(12))
final_filename = fname+extension
output_file = open(final_filename, 'w')
output_file.write(bulk_excel['body'])
Now I have also tried to write open(final_filename, 'wb') but, it did not work as intended.
Thanks in advance.
Related
I am trying to create an API function, that takes in .csv file (uploaded) and opens it as pandas DataFrame. Like that:
from fastapi import FastAPI
from fastapi import UploadFile, Query, Form
import pandas as pd
app = FastAPI()
#app.post("/check")
def foo(file: UploadFile):
df = pd.read_csv(file.file)
return len(df)
Then, I am invoking my API:
import requests
url = 'http://127.0.0.1:8000/check'
file = {'file': open('data/ny_pollution_events.csv', 'rb')}
resp = requests.post(url=url, files=file)
print(resp.json())
But I got such error: FileNotFoundError: [Errno 2] No such file or directory: 'ny_pollution_events.csv'
As far as I understand from doc pandas is able to read .csv file from file-like object, which file.file is supposed to be. But it seems, that here in read_csv() method pandas obtains name (not a file object itself) and tries to find it locally.
Am I doing something wrong?
Can I somehow implement this logic?
To read the file in pandas, the file must be stored on your PC. Don't forget to import shutil. if you don't need the file to be stored on your PC, delete it using os.remove(filepath).
if not file.filename.lower().endswith(('.csv',".xlsx",".xls")):
return 404,"Please upload xlsx,csv or xls file."
if file.filename.lower().endswith(".csv"):
extension = ".csv"
elif file.filename.lower().endswith(".xlsx"):
extension = ".xlsx"
elif file.filename.lower().endswith(".xls"):
extension = ".xls"
# eventid = datetime.datetime.now().strftime('%Y%m-%d%H-%M%S-') + str(uuid4())
filepath = "location where you want to store file"+ extension
with open(filepath, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
try:
if filepath.endswith(".csv"):
df = pd.read_csv(filepath)
else:
df = pd.read_excel(filepath)
except:
return 401, "File is not proper"
I need to put together the following scenario.
What libraries or frameworks should I use to complete this scenario?
I have basic knowledge of Python.
I found the following way to implement a 'file upload and delete process' in SharePoint with the use of few python codes.
You will need the two python libraries 'sharepoint' and 'shareplum'.
To install 'sharepoint': pip install sharepoint
To install 'shareplum': pip install SharePlum
Then you can implement the main code to upload and delete the files
as following:
sharepoint.py
from shareplum import Site, Office365
from shareplum.site import Version
import json, os
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
config_path = '\\'.join([ROOT_DIR, 'config.json'])
# read config file
with open(config_path) as config_file:
config = json.load(config_file)
config = config['share_point']
USERNAME = config['user']
PASSWORD = config['password']
SHAREPOINT_URL = config['url']
SHAREPOINT_SITE = config['site']
SHAREPOINT_DOC = config['doc_library']
class SharePoint:
def auth(self):
self.authcookie = Office365(SHAREPOINT_URL, username=USERNAME, password=PASSWORD).GetCookies()
self.site = Site(SHAREPOINT_SITE, version=Version.v365, authcookie=self.authcookie)
return self.site
def connect_folder(self, folder_name):
self.auth_site = self.auth()
self.sharepoint_dir = '/'.join([SHAREPOINT_DOC, folder_name])
self.folder = self.auth_site.Folder(self.sharepoint_dir)
return self.folder
def upload_file(self, file, file_name, folder_name):
self._folder = self.connect_folder(folder_name)
with open(file, mode='rb') as file_obj:
file_content = file_obj.read()
self._folder.upload_file(file_content, file_name)
def delete_file(self, file_name, folder_name):
self._folder = self.connect_folder(folder_name)
self._folder.delete_file(file_name)
I save the above code in sharepoint.py file.
Then use the methods in the following way. I import the above methods
from the above 'sharepoint.py' file and use as follows:
updelsharepoint.py
from sharepoint import SharePoint
#i.e - file_dir_path = r'E:\project\file_to_be_uploaded.xlsx'
file_dir_path = r'E:\File_Path\File_Name_with_extension'
# this will be the file name that it will be saved in SharePoint as
file_name = 'File_Name_with_extension'
# The folder in SharePoint that it will be saved under
folder_name = 'SampleUploads'
# upload file
SharePoint().upload_file(file_dir_path, file_name, folder_name)
# delete file
SharePoint().delete_file(file_name, folder_name)
Finally, to configure your email, password, and SharePoint account you have to create a
'config.json' as follows.
config.json
{
"share_point":
{
"user": "{email}",
"password": "{password}",
"url": "https://{domain}.sharepoint.com",
"site": "https://{domain}.sharepoint.com/sites/{Name}/",
"doc_library": "Shared Documents/{Document}"
}
}
I hope this will help you to solve your problem. You can further improve the above sample code and implement a better code than what I shared.
https://fastapi.tiangolo.com/tutorial/request-files/
*Solved Below *
I've gotten an appropriately sized array of bytes and I'm not sure how to parse it correctly to save received form file data.
Almost working:
#app.post("/uploadfiles/")
async def create_files(files: List[bytes] = File(...)):
out_file = open("files/1.txt", "w") # open for [w]riting as [b]inary
out_file.write( str([(file) for file in files]))
out_file.close()
return {"file_sizes": [len(file) for file in files]}
The script results in a file that is no longer a readable .png. I assume I'm using the libraries incorrectly but I'm not sure which to start with: HTMLResponse, FastAPI, multipart or List maybe? Any ideas or docs on how to properly parse these bytes back together again?
Edit
Per a break and once over review of the FastAPI docs, I spotted that this particular array of byte data is multipart/form-data data (so maybe I'll find a python library for reading form data/images):
Here is a small example (first ~100chars) of the data that is generated:
[b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x03\xe8\x00\x00\x01\xf4\x08\x06\x00\x00\x00\xae(\x07-\x00\x00\x01\x86iCCPICC profile\x00\x00(\x91}\x91=H\
I did it!!!
The secret was pythons native byte recognition libraries and order of ops!!
#app.post("/uploadfiles/")
async def create_files(files: bytes = File(...)):
out_file = open("files/1.jpg", "wb") # open for [w]riting as [b]inary
out_file.write( bytes([(file) for file in files]))
out_file.close()
Still work to do on the file naming system :) GL Everyone!
Edit 2
Since the below answer didn't function, I scripted a file name appender:
#app.post("/uploadfiles/")
async def create_files(files: bytes = File(...)):
with open('C:/data/sample.txt', 'r', encoding='utf-8') as g:
data=g.readlines()
for line in data:
counter = int(line)
with open('C:/data/sample.txt', 'w') as f:
counter = counter + 1
f.write(str(counter))
out_file = open("files/" + str(counter) + ".jpg", "wb") # open for [w]riting as [b]inary
out_file.write( bytes([(file) for file in files]))
out_file.close()
Thanks for the help everyone! Happy hacking :^)
Edit 3
I've been informed that the method of which I am posting could be incorrect practice in conjunction with FastAPI so to better understand this I am posting the relevant javascript that posts to the backend:
Here is the relevant code from my reactjs form post script:
onSubmit = (e) => {
e.preventDefault();
const formData = new FormData();
if (this.state.images != null) {
var form = document.getElementById("apiupform");
document.getElementById("apiupform").hidden = true;
Array.from(this.state.images).forEach((image) => {
formData.append("files", image);
});
axios
.post(`https://****.com/uploadfiles/`, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
Thank you everyone for the help with this work. I will update it as I find improvements :)
You can save the uploaded files this way,
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
#app.post("/upload-file/")
async def create_upload_file(uploaded_file: UploadFile = File(...)):
file_location = f"files/{uploaded_file.filename}"
with open(file_location, "wb+") as file_object:
file_object.write(uploaded_file.file.read())
return {"info": f"file '{uploaded_file.filename}' saved at '{file_location}'"}
OR,
maybe a more efficient way using the shutil.copyfileobj(...) method as,
import shutil
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
#app.post("/upload-file/")
async def create_upload_file(uploaded_file: UploadFile = File(...)):
file_location = f"files/{uploaded_file.filename}"
with open(file_location, "wb+") as file_object:
shutil.copyfileobj(uploaded_file.file, file_object)
return {"info": f"file '{uploaded_file.filename}' saved at '{file_location}'"}
Sample Swagger
I would use an asynchronous library like aiofiles for file operations.
Since you are running your app inside an event loop the file writing operation will block the entire execution of your app.
import aiofiles
#app.post("/uploadfiles/")
async def create_files(files: bytes = File(...)):
async with aiofiles.open("files/1.jpg", "wb") as f:
await f.write(bytes([(file) for file in files]))
this is what worked for me..
#app.post("/fileUpload/")
def fileUpload(ufile: UploadFile = File(...)):
s = str(ufile.file.read(), 'utf-8')
with open(ufile.filename, "w+") as buffer:
buffer.write(s)
I have a flask app and am trying to set up a download where the file that is downloaded starts with a "." (dot, or period). The code is listed below, but I have tried several methods, each simply removes the "dot" at the beginning. I feel like this should be simple, but I am stuck. Any help would be appreciated.
Thanks
#app.route('/.blah.auth', methods=['GET'])
def downloadBlahAuth(roleLevel = app.config["PAGE_ROLE_LEVELS"][".blah.auth"]):
"""
filePath = os.path.join(app.config["DOWNLOADS_FOLDER"], ".blah.auth");
theFile = open(filePath, "r")
fileText = theFile.read()
theFile.close()
strIO = StringIO.StringIO()
strIO.write(fileText)
strIO.seek(0)
return send_file(strIO, attachment_filename=".blah.auth", as_attachment=True)
"""
fileName = ".blah.auth"
return send_from_directory(directory=app.config["DOWNLOADS_FOLDER"], filename=fileName)
I am trying to make a downloadable text file on the fly, i think i have achieved this but when i run the code i get a permission denied error.
also when i open this text file, does it get created anywhere in the file system? as i dont want to store these files, just create them and have them downloaded to users machine
IOError at /networks/configs/STR-RTR-01/7
[Errno 13] Permission denied: u'STR-CARD-RTR-01.txt'
config:
def configs(request, device, site_id):
site = get_object_or_404(ShowroomConfigData, pk=site_id)
config_template = get_object_or_404(ConfigTemplates, device_name=device)
file_name = device[:4] + site.location.upper()[:4] + "-" + device[4:] + ".txt"
device_config = None
with open(file_name, 'w') as config_file:
device_config = env.from_string(config_template.config)
device_config.stream(
STR = site.location.upper()[:4],
IP = site.subnet,
BGPASNO = site.bgp_as,
LOIP = site.r1_loopback_ip,
Location = site.location,
Date = site.opening_date,
).dump(config_file)
return render(request, file_name, {
})
If the goal is to provide a link where the user can download a file which is automatically generated, there's no need to write anything to disk.
You can just build the desired content in a Python string, and use the Content-Disposition header to suggest that a user's browser should download the file rather than display it, and its filename parameter to specify a default filename for the user to save the file as.
A slightly simpler example of a view function which does this...
from django.http import HttpResponse
def get_config_file(request):
filename = 'config.txt'
content = 'This is the content of my config file'
content_type = 'text/plain'
content_disposition = 'attachment; filename=%r' % filename
response = HttpResponse(content, content_type=content_type)
response['Content-Disposition'] = content_disposition
return response