Django,Heroku,boto:直接将文件上传到Google云存储

在部署在Heroku上的Django项目中,我曾经通过boto将文件上传到Google云存储。 但是,最近我不得不上传大文件,这会导致Heroku超时。

我正在关注Heroku有关将文件直接上传到S3的文档,并按照以下方式进行自定义:

蟒蛇:

conn = boto.connect_gs(gs_access_key_id=GS_ACCESS_KEY,
                       gs_secret_access_key=GS_SECRET_KEY)
presignedUrl = conn.generate_url(expires_in=3600, method='PUT', bucket=<bucketName>, key=<fileName>, force_http=True)

JS:

url = 'https://<bucketName>.storage.googleapis.com/<fileName>?Signature=...&Expires=1471451569&GoogleAccessId=...'; // "presignUrl"

postData = new FormData();
postData.append(...);
...

$.ajax({
  url: url,
  type: 'PUT',
  data: postData,
  processData: false,
  contentType: false,
});

我收到以下错误消息:

XMLHttpRequest cannot load http:/...  Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access.

编辑:

gsutil cors get gs://<bucketName>的输出gsutil cors get gs://<bucketName>

[{"maxAgeSeconds": 3600, "method": ["GET", "POST", "HEAD", "DELETE", "PUT"], "origin": ["*"], "responseHeader": ["Content-Type"]}]

看起来CORS是好的。 那么,我该如何解决这个问题呢? 谢谢。

编辑2:

来自Firefox的OPTION请求的标头:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.5,en;q=0.3
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: <bucketName>.storage.googleapis.com
Origin: http://localhost:8000
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:48.0) Gecko/20100101 Firefox/48.0

来自Chrome的OPTION请求标题:

Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:zh-TW,zh;q=0.8,en;q=0.6,en-US;q=0.4,zh-CN;q=0.2
Access-Control-Request-Headers:
Access-Control-Request-Method:PUT
Connection:keep-alive
Host:directupload.storage.googleapis.com
Origin:http://localhost:8000
Referer:http://localhost:8000/
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
X-Client-Data:CIe2yQEIprbJAQjznMoB

标题问题不是来自您的应用程序,我认为它来自云存储分区。 设置api时我遇到同样的问题,您发布的资源缺少标题。

https://cloud.google.com/storage/docs/cross-origin

尽管可用于防止恶意行为,但此安全措施还可防止已知来源之间的有用合法交互。 例如,example.appspot.com上Google App Engine托管的页面上的脚本可能需要使用存储在云存储存储桶中的静态资源(example.storage.googleapis.com)。 但是,因为从浏览器的角度来看,这两个来源是不同的,所以浏览器将不允许example.appspot.com的脚本使用XMLHttpRequest从example.storage.googleapis.com获取资源,因为所提取的资源来自不同的起源。

所以看起来你需要配置桶以允许Cors请求。 谷歌文档显示下面的代码从谷歌cli运行。

https://cloud.google.com/storage/docs/cross-origin#Configuring-CORS-on-a-Bucket

gsutil cors set cors-json-file.json gs://example

[
    {
      "origin": ["http://mysite.heroku.com"],
      "responseHeader": ["Content-Type"],
      "method": ["GET", "HEAD", "DELETE", "PUT"],
      "maxAgeSeconds": 3600
    }
]

这将允许您获取,上传和删除内容。 希望有所帮助。


根据编辑2中的信息,请求有问题。 印前检查(OPTIONS)请求包括头部ACCESS-CONTROL-REQUEST-HEADER 。 这不是有效的CORS标题。 正确的头部是ACCESS-CONTROL-REQUEST-HEADERS ,最后注意'S'。

即使头部是正确的,它也不应该请求access-control-allow-origin头部的授权。 ACCESS-CONTROL-ALLOW-ORIGIN不是从客户端发送的标题。 当服务器收到预检请求时,它将自动发送到服务器到客户端的响应中。 除非客户端/浏览器获得授权浏览器文档当前来源于预检请求中的跨源服务器的ACCESS-CONTROL-ALLOW-ORIGIN标头,否则客户端/浏览器将不允许跨源PUT请求。

错误标题的出现似乎与您收到的错误响应有很好的关联。 但是,它看起来像头可能不是在您的原始代码中,它看起来像稍后添加它(根据您的意见)。 一定要把这个头配置出来,这肯定是不正确的。

所以我对这个标题来自哪里有点困惑,但我认为它是你问题的根源。

看起来你正在使用jQuery来制作AJAX PUT请求。 我真的可以建议的是确保你没有在你的JS代码中的某个地方调用$ .ajaxSetup()来配置错误的头文件。


经过这么多的尝试和错误,我想出了以下内容。 程序运行,但是,有时/一些上传的图像不可见; 其他时间他们都可以。 我不知道为什么会发生这种情况。

我想征求更多的想法,为什么文件上传可以,但一些图像已损坏。

gsutil命令:

gsutil cors set cors.json gs://<bucketName>
gsutil defacl ch -u allUsers:R gs://<bucketName>

cors.json文件的内容:

[
    {
        "origin": ["*"],
        "responseHeader": ["Content-Type"],
        "method": ["GET", "POST", "HEAD", "DELETE", "PUT"],
        "maxAgeSeconds": 3600
    }
]

HTML:

<p id=status>Choose your avatar:</p>
<input id=fileInput type=file>

JavaScript的:

$(document).on('change', '#fileInput', function() {
  var $this = $(this);
  var file = $this[0].files[0];

  $.ajax({
    url: 'upload/sign/?fileName=' + file.name + '&contentType=' + file.type,
    type: 'GET'
  })
  .done(function(data) {
    var response = JSON.parse(data);
    uploadFile(file, response.presignedUrl, response.url, response.contentType)
  })
  .fail(function() {
    alert('Unable to obtain a signed URL.');
  });
});

function uploadFile(file, presignedUrl, url, contentType) {
  var postData = new FormData();
  postData.append('file', file);

  $.ajax({
    url: presignedUrl,
    type: 'PUT',
    data: postData,
    headers: {
      'Content-Type': contentType,
    },
    processData: false,
    contentType: false
  })
  .done(function() {
    alert('File upload successful');
  })
  .fail(function() {
    alert('Unable to upload the file.');
  });
}

Django的:

项目的urls.py

urlpatterns = [
    ...
    url(r'upload/', include('upload.urls', namespace='upload')),
]

应用的urls.py

urlpatterns = [
    url(r'^$', views.upload, name='upload'),
    url(r'^sign/', views.sign, name='sign'),
]

views.py:

def upload(request):
    # ... render the template


def sign(request):
    fileName = request.GET.get('fileName')
    contentType = request.GET.get('contentType') 
    conn = boto.connect_gs(gs_access_key_id=GS_ACCESS_KEY,
                           gs_secret_access_key=GS_SECRET_KEY)
    presignedUrl = conn.generate_url(3600, 'PUT', GS_BUCKET_NAME, fileName, headers={'Content-Type':contentType})
    return HttpResponse(
        json.dumps({
            'presignedUrl': presignedUrl,
            'url': GS_URL + fileName,
            'contentType': contentType
        })
    )
链接地址: http://www.djcxy.com/p/92897.html

上一篇: Django, Heroku, boto: direct file upload to Google cloud storage

下一篇: ES6 import error handling