How to link external storage (BackBlaze) and MySQL using Python (updated)

58 views Asked by At

Quick intro to the modules: app.py - runs basic flask app, which imports file to MySQL database using SQLAlchemy. storage.py - has a connection to BackBlaze, which picks up a file from static folder and sends it for storage, storage link gets generated from BackBlaze.

I'm trying to amend the code so, once I upload the file to flask via app.py, file link is saved in MySQL and actual file is sent to BackBlaze. Can you please assist to link two modules?

app.py

from io import BytesIO
from flask_migrate import Migrate
from flask import Flask, render_template, request, send_file
from flask_sqlalchemy import SQLAlchemy
from storage import backblazeload

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:password@localhost/fof'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
migrate = Migrate(app, db)


class Trades(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    filename = db.Column(db.String(50))
    data = db.Column(db.LargeBinary)
    link = db.Column(db.String(200))


@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        file = request.files['file']
        upload = Trades(filename=file.filename, data=file.read())
        db.session.add(upload)
        db.session.commit()

        # Generate the BackBlaze link and save it to the database
        link = backblazeload(file)
        upload.link = link
        db.session.commit()

        return f'Uploaded: {file.filename}'
    return render_template('index.html')


@app.route('/download/<upload_id>')
def download(upload_id):
    upload = Trades.query.filter_by(id=upload_id).first()
    return send_file(upload.data, attachment_filename=upload.filename, as_attachment=True)


if __name__ == '__main__':
    app.run(debug=True)

storage.py

import os
from dotenv import load_dotenv
from b2sdk.v2 import InMemoryAccountInfo, B2Api
from io import BytesIO


def backblazeload(file):
    load_dotenv()

    info = InMemoryAccountInfo()  # store credentials, tokens and cache in memory
    b2_api = B2Api(info)

    application_key_id = "app_id"
    application_key = "app_key"
    b2_api.authorize_account("production", application_key_id, application_key)

    bucket = b2_api.get_bucket_by_name("pickabucketname")

    file_data = BytesIO(file.read())
    b2_file_name = file.filename
    file_info = {'how': 'good-file'}

    uploaded_file_info = bucket.upload_bytes(
        file_data,
        file_name=b2_file_name,
        file_infos=file_info
    )

    uploaded_file_id = uploaded_file_info['fileId']

    print(b2_api.get_download_url_for_fileid(uploaded_file_id))

I'm getting below error now:

TypeError: object of type '_io.BytesIO' has no len()

1

There are 1 answers

0
metadaddy On

Two issues in storage.py are:

  • As mentioned in the commends, you're passing a BytesIO to bucket.upload_bytes(), when it just wants the bytes
  • You're treating the FileVersion object returned by bucket.upload_bytes() as if it were a dict, causing the 'FileVersion' object is not subscriptable error.

This should work for you:

import os
from dotenv import load_dotenv
from b2sdk.v2 import InMemoryAccountInfo, B2Api
from io import BytesIO


def backblazeload(file):
    load_dotenv()

    info = InMemoryAccountInfo()  # store credentials, tokens and cache in memory
    b2_api = B2Api(info)

    application_key_id = "app_id"
    application_key = "app_key"
    b2_api.authorize_account("production", application_key_id, application_key)

    bucket = b2_api.get_bucket_by_name("pickabucketname")

    file_data = file.read()
    b2_file_name = file.filename
    file_info = {'how': 'good-file'}

    uploaded_file_info = bucket.upload_bytes(
        file_data,
        file_name=b2_file_name,
        file_infos=file_info
    )

    uploaded_file_id = uploaded_file_info.id_

    print(b2_api.get_download_url_for_fileid(uploaded_file_id))