struggling with trying to write a MIDIUtil file in my Flask App connecting to an s3 server.
In a local instance, it's no sweat:
LOCAL_UPLOAD_FOLDER = './_static/uploads/MIDI_files/'
file_name = "NAME.mid"
file_path = f'{LOCAL_UPLOAD_FOLDER}{file_name}'
MyMIDI = MIDIFile(1)
with open(file_path, "wb") as output_file:
MyMIDI.writeFile(output_file)
However, I'm not sure how to apply this to an s3 resource, here's my instantiations...
def get_upload_folder(UPLOAD_FOLDER=None, UPLOAD_FOLDER_KEY=None,
client_resource=None, client=None):
""" Determines How to Upload / Send File for Download """
# Flask Cloud Settings - Upload Folder
if os.getenv('CONTEXT') == 'CLOUD':
# Client Side
UPLOAD_FOLDER_TYPE = 'CLOUD'
session = boto3.session.Session()
client = session.client(
's3', endpoint_url=os.getenv('ENDPOINT_URL'),
config=botocore.config.Config(s3={'addressing_style': 'virtual'}),
region_name=os.getenv('REGION_NAME'), aws_access_key_id=os.getenv('SECRET_ID'),
aws_secret_access_key=os.getenv('SECRET_KEY')
)
# Resource Side
client_resource = boto3.resource(
's3', endpoint_url='https://nyc3.digitaloceanspaces.com',
config=botocore.config.Config(s3={'addressing_style': 'virtual'}),
region_name='nyc3', aws_access_key_id=os.getenv('SECRET_ID'),
aws_secret_access_key=os.getenv('SECRET_KEY')
)
UPLOAD_FOLDER, UPLOAD_FOLDER_KEY = 'MY_BUCKET', 'uploads/MIDI_files/'
return UPLOAD_FOLDER_TYPE, UPLOAD_FOLDER, UPLOAD_FOLDER_KEY, client_resource, client
Thus far, I've tried:
with open(file_path, 'wb') as output_file:
MyMIDI.writeFile(output_file)
client.download_fileobj(UPLOAD_FOLDER, 'OBJECT_NAME', output_file)
and a wealth of other .put_object
combinations with client and client_resource boto3 objects...
I'm thinking that my problem lies within:
- The
writeFile(filehandler)
of theMIDIUtil.Midifile
Perhaps this function is closing the MIDI binary stream DATA before I could put_object
into a s3 BODY=
? Maybe I need to parse the binary data through a Bytes(IO) / stream object..?
OR
- Trying to achieve a writeable directory using my s3 object.
Perhaps I could assign the s3 UPLOAD_FOLDER better... I'm just not sure how I would make this connection in FLASK...
app.config['UPLOAD_FOLDER'] = client.Object(
Bucket=UPLOAD_FOLDER, Key=UPLOAD_FOLDER_KEY,
ACL='private'
)
Any help is appreciated! Feel like I may have gotten closer with this method... It does actually write to the s3 Bucket, so I might ditch worrying about grabbing a usable URL, but the MIDI file is corrupted and blank =(
file_path = f'{UPLOAD_FOLDER_KEY}{file_name}'
response = client.generate_presigned_post(UPLOAD_FOLDER,
file_name,
ExpiresIn=3600)
post_url = response['url']
data = response['fields']
key = data['key']
with open(file_name, 'wb') as f:
http_response = requests.post(url=post_url, data=data,
files={file_name: MyMIDI.writeFile(f)})
print(response)
produces:
{'url': 'ENDPOINT_URL', 'fields': {'key': 'files(from above)', 'x-amz-algorithm': 'STUFF', 'x-amz-credential': 'STUFF', 'x-amz-date': 'STUFF', 'policy': 'STUFF', 'x-amz-signature': 'STUFF'}}```
Just not positive if I can pull a URL from this to redirect to... Trying to dissolve this Article on S3 File Uploads for an answer.
CodePudding user response:
The Solution:
I can't EXACTLY explain why; but I think the issue came along with the interaction between MyMIDI.writefile(filehandler)
and boto3
functions. I think it has something to do within the .write()
and .close()
being nested with the .writefile()
of the MIDIUtil
package, WHILE having to simultaneously generate the byte data for s3's
Body
parameter. So here's my workaround...
# Working Version on S3 Deployment
# Assign generate_presigned_post to variable
response = client.generate_presigned_post(UPLOAD_FOLDER,
file_name,
ExpiresIn=3600)
# Have MIDIUtil write / close the file within writefile
with open(file_name, 'wb') as file:
MIDI_FILE = MyMIDI.writeFile(file)
# Read the written binary contents for the s3 Body; assign to a variable (content)
f = open(file_name, 'rb')
content = f.read()
# Stage the object with its file_name and s3 Bucket(UPLOAD_FOLDER)
MIDI_Object = client_resource.Object(UPLOAD_FOLDER, file_name)
# Write to the s3 Bucket using put
MIDI_Object.put(Body=content)
VOILA! It's no longer blank in my S3 Bucket and available for download!! :D
CodePudding user response:
This code will not work as intended:
with open(file_path, 'wb') as output_file:
MyMIDI.writeFile(output_file)
client.upload_file(UPLOAD_FOLDER, 'OBJECT_NAME', output_file)
The problem with this code is that you are using a context manager (with
) and the context manager does not close the output file until you exit the context manager. Consequently, the file contents are not flushed to disk at the point you attempt to upload the file to S3.
It needs to be written like this:
with open(file_path, 'wb') as output_file:
MyMIDI.writeFile(output_file)
client.upload_file(UPLOAD_FOLDER, 'OBJECT_NAME', output_file)