【问题标题】:How to upload file from Google App Engine to Google Cloud Storage using Python Flask?如何使用 Python Flask 将文件从 Google App Engine 上传到 Google Cloud Storage?
【发布时间】:2021-11-05 06:44:05
【问题描述】:

我想将文件从 Google App Engine 上传到 Google Cloud Storage 我正在使用 Pyhton 3.8 和 Flask。

app.yaml:

runtime: python38

requirements.txt

Flask==2.0.0;
google-cloud
google-cloud-storage

我尝试使用 Flask (https://www.tutorialspoint.com/flask/flask_file_uploading.htm) 将文件上传到 /tmp(在 App Engine 临时存储中 (https://cloud.google.com/appengine/docs/standard/python3/using-temp-files))

然后我尝试使用以下代码示例从临时存储上传到 Google Cloud Storage:(https://cloud.google.com/storage/docs/uploading-objects#storage-upload-object-code-sample)

但这给了我这个错误:

OSError: [Errno 30] Read-only file system: 'file.xlsx'

我也尝试过这个示例:(https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/appengine/standard/storage/api-client/main.py) 并给了我同样的错误。

然后我尝试使用 gsutil (https://cloud.google.com/storage/docs/uploading-objects#gsutil) 使用 subprocess.check_output (Return value of x = os.system(..)) 仍然给我同样的错误。

谁能帮助我?我该怎么办?

############################################## ##

解决了

############################################## ##

谢谢Edo Akse,我的错误是使用“/tmp”而不是“/tmp/”,非常感谢帮助我。

以下是完整的代码。

from flask import Flask, request
from werkzeug.utils import secure_filename
from google.cloud import storage
import os

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = "/tmp/"

@app.route('/')
def index():
    page = '''
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input name="file" class="custom-file-input" type="file">
        <button type="submit" class="btn btn-success">Submit</button>
    </form>
    '''
    return page

@app.route('/upload',methods=["POST"])
def upload():
    try:
        if request.method == 'POST':
            f = request.files['file']
            f.save(os.path.join(app.config['UPLOAD_FOLDER'],secure_filename(f.filename)))
            
            #Start Do Main Process Here
            TMP_FILE_NAME = os.path.join(app.config['UPLOAD_FOLDER'],secure_filename(f.filename))
            BUCKET_NAME = 'fsample123'
            result = upload_blob(BUCKET_NAME, TMP_FILE_NAME, f"tmp/{secure_filename(f.filename)}")
            #End Do Main Process Here
            return result
            
    except Exception as e:
        return str(e)

def upload_blob(bucket_name, source_file_name, destination_blob_name):
    """Uploads a file to the bucket."""
    # SOURCE:
    # https://cloud.google.com/storage/docs/uploading-objects#uploading-an-object

    # The ID of your GCS bucket
    # bucket_name = "your-bucket-name"
    # The path to your file to upload
    # source_file_name = "local/path/to/file"
    # The ID of your GCS object
    # destination_blob_name = "storage-object-name"

    storage_client = storage.Client()
    bucket = storage_client.bucket(bucket_name)
    blob = bucket.blob(destination_blob_name)
    blob.upload_from_filename(source_file_name)
    return "File {} uploaded to {}.".format(source_file_name, destination_blob_name)

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

【问题讨论】:

  • 我认为您的问题可能不在于 GCS 库,而在于存储临时文件的位置。您没有指定 GAE Flex 或 Standard,但两者都处理临时文件存储。例如,请参阅this doc 以了解标准
  • 刚刚注意到您处理的是 GAE 标准,所以上面的链接适用...
  • 是的,我使用的是 GAE 标准,那么我应该怎么做才能从 GAE 上传到 GCS?如果问题是临时文件存储,我应该将我的文件临时上传到 GCS 哪里?

标签: python flask google-app-engine google-cloud-storage


【解决方案1】:

所以问题不在于上传到 GCS,而在于您创建的临时文件。

The only directory that has write access/tmp 并且注意它不是在 GAE 实例之间共享的,它会在实例重新启动时消失...

from google.cloud import storage


def upload_blob(bucket_name, source_file_name, destination_blob_name):
    """Uploads a file to the bucket."""
    # SOURCE:
    # https://cloud.google.com/storage/docs/uploading-objects#uploading-an-object

    # The ID of your GCS bucket
    # bucket_name = "your-bucket-name"
    # The path to your file to upload
    # source_file_name = "local/path/to/file"
    # The ID of your GCS object
    # destination_blob_name = "storage-object-name"

    storage_client = storage.Client()
    bucket = storage_client.bucket(bucket_name)
    blob = bucket.blob(destination_blob_name)
    blob.upload_from_filename(source_file_name)
    print(
        "File {} uploaded to {}.".format(
            source_file_name, destination_blob_name
        )
    )


TMP_PATH = "/tmp/"
TMP_FILE_NAME = f"{TMP_PATH}file.xlsx"
BUCKET_NAME = '<your-bucket-name>'


with open(TMP_FILE_NAME, "w") as outfile:
    outfile.write("write file action goes here")
upload_blob(BUCKET_NAME, TMP_FILE_NAME, "Target file path goes here...")

【讨论】: