2011-04-20 8 views
18

저는 REST 스타일 인터페이스를 구현 중이며 HTTP PUT 요청을 통해 파일을 업로드 (업로드를 통해)하고 싶습니다. TemporaryUploadedFile 또는 InMemoryUploadedFile을 만들고 싶습니다. 그런 다음 모델의 일부인 개체의 기존 FileField.save()에 전달하여 파일을 저장합니다.Django에서 PUT 요청을 통해 파일 업로드를 처리하는 방법은 무엇입니까?

파일 업로드 부분을 처리하는 방법에 대해서는 잘 모르겠습니다. 특히,이 요청은 풋 요청이므로 PUT 요청에 존재하지 않으므로 request.FILES에 액세스 할 수 없습니다.

그래서, 몇 가지 질문 :

  • 내가의 HttpRequest 클래스에서 파일 업로드를 처리하는 특정 부분을 기존 기능을 활용할 수 있습니까? 직접 PUT 멀티 파트 MIME 요청이 아니라는 것을 알고 있으므로 그렇게 생각하지는 않지만 묻는 가치가 있습니다.
  • 전송되는 MIME 유형을 어떻게 추론 할 수 있습니까? 필자가 올바르게 이해했다면 PUT 본문은 단순히 서곡이없는 파일 일뿐입니다. 따라서 사용자가 헤더에 MIME 형식을 지정해야합니까?
  • 이 방법을 대용량 데이터로 확장하는 방법은 무엇입니까? 매우 비효율적이기 때문에 나는 그것을 메모리에 모두 읽고 싶지 않습니다. 이상적으로 나는 TemporaryUploadFile 및 관련 코드가하는 것을 할 것입니다 - 한 번에 부분을 작성합니까?

은 내가 POST 요청으로 PUT 처리에 트릭 장고 this code sample에서 살펴 보았다. 그래도 맞다면 형식으로 인코딩 된 데이터 만 처리 할 것입니다. 이것은 REST이므로 최상의 솔루션은 양식의 인코딩 된 데이터가 존재하지 않는다는 것입니다. 그러나 mime (멀티 파트가 아닌)을 어떻게 든 사용하는 것에 대한 적절한 조언을 듣게되어 기쁩니다 (하지만 업로드에는 하나의 파일 만 포함해야합니다).

장고 1.3이 허용됩니다. 그래서 나는 request.raw_post_data 또는 request.read() (또는 더 나은 접근 방법 중 하나)으로 무엇인가를 할 수 있습니다. 어떤 아이디어?

답변

8

장고 1.3이 허용됩니다. 따라서 은 request.raw_post_data 또는 request.read() (또는 일부 더 나은 액세스 방법 인 )로 처리 할 수 ​​있습니다. 어떤 아이디어?

당신은 request.raw_post_data에 접촉되지 않도록하려면 - 당신은 파일 업로드 말하는 건가하는 것은 매우 많은 양이 될 수있는 메모리에 전체 요청 본문을 읽는 의미하는, 그래서 request.read() 갈 방법입니다. Django < = 1.2에서도이 작업을 수행 할 수 있지만, HttpRequest에서 개인 인터페이스를 사용하는 올바른 방법을 알아내는 것을 의미합니다. 그러면 코드가 Django> = 1.3.

나는 싶은 것은 existing file upload behaviour parts of the MultiPartParser class 복제하는 것을 좋을 것

:

  1. 요청의 내용 길이를 결정합니다 (기본적으로 MemoryFileUploadHandler & TemporaryFileUploadHandler이 될 것입니다) request.upload_handlers에서 업로드 투수 검색 (콘텐츠 길이 검색 HttpRequest 또는 MultiPartParser 올바른 방법을 참조하십시오.)
  2. 클라이언트가 url의 마지막 경로 부분을 사용하여 지정하도록하거나 클라이언트가 "filename ="부분에 the Content-Disposition header을 지정하도록하여 업로드 된 파일의 파일 이름을 결정합니다. 각 처리기에 대한
  3. (필드 이름을 조롱) 관련 인수
  4. request.read()를 사용하여 각 청크에 대한 handler.receive_data_chunk()를 호출 덩어리에 요청 본문 읽기와 handler.new_file를 호출합니다.
  5. 각 처리기 호출 handler.file_complete()에 대해 값을 반환하면 업로드 된 파일입니다.
I가 전송되는 것을 의 MIME 타입을 추론 할 수있는 방법

? 내가 맞다면 PUT 본문은 단순히 서곡이없는 파일 일뿐입니다. 따라서 사용자가 헤더에 MIME 유형을 지정해야합니까?

클라이언트가 Content-Type 헤더에서 지정하도록하거나 python's mimetype module을 사용하여 미디어 유형을 추측 할 수 있습니다.

나는 이걸 어떻게 사용하는지 알아내는 데 흥미가있을 것입니다. 그것은 제가 스스로를 들여다 본 것의 의미였습니다. 어떻게 움직이는 지 알려 주시면 감사하겠습니다. Ninefingers의 요청에 따라에 의해


편집, 이것은 내가 한하고, 상기와 장고 소스에 전적으로 기반 것입니다.

upload_handlers = request.upload_handlers 
content_type = str(request.META.get('CONTENT_TYPE', "")) 
content_length = int(request.META.get('CONTENT_LENGTH', 0)) 

if content_type == "": 
    return HttpResponse(status=400) 
if content_length == 0: 
    # both returned 0 
    return HttpResponse(status=400) 

content_type = content_type.split(";")[0].strip() 
try: 
    charset = content_type.split(";")[1].strip() 
except IndexError: 
    charset = "" 

# we can get the file name via the path, we don't actually 
file_name = path.split("/")[-1:][0] 
field_name = file_name 

여기에서 API를 정의 했으므로 브라우저 간 지원은 중요하지 않습니다. 내 의정서에 관한 한 올바른 정보를 제공하지 않으면 깨진 요청입니다. image/jpeg; charset=binary을 사용할지 또는 존재하지 않는 문자 세트를 허용할지 여부에 대해 두 가지 생각을하고 있습니다. 어쨌든, 클라이언트 측 책임으로 Content-Type을 유효하게 설정하고 있습니다.

마찬가지로 내 프로토콜의 경우 파일 이름이 전달됩니다. field_name 매개 변수가 무엇인지, 소스가 많은 단서를 제공하지 않았는지 확실하지 않습니다.

아래에서 일어나는 일은 실제로 보이는 것보다 훨씬 간단합니다. 처리기가 원시 입력을 처리 할 것인지 각 처리기에 요청합니다. 위 국가의 저자는 기본적으로 MemoryFileUploadHandler & TemporaryFileUploadHandler입니다. 글쎄, new_file을 만들지 묻는 메시지가 나타나면 MemoryFileUploadHandler이 나오고 다양한 설정에 따라 파일을 처리할지 여부를 결정합니다. 그것이 진행될 것이라고 결정하면 예외를 throw합니다. 그렇지 않으면 파일을 만들지 않고 다른 핸들러를 인계받습니다.

counters의 목적은 무엇인지 모르겠지만 소스에서 보관 해 두었습니다. 나머지는 간단해야합니다.

if request.content_type.startswith('multipart'): 
    put, files = request.parse_file_upload(request.META, request) 
    request.FILES.update(files) 
    request.PUT = put.dict() 
else: 
    request.PUT = QueryDict(request.body).dict() 

이처럼 파일 및 기타 데이터에 액세스 할 수 있도록 :이 같은 주어진 솔루션을 수정

counters = [0]*len(upload_handlers) 

for handler in upload_handlers: 
    result = handler.handle_raw_input("",request.META,content_length,"","") 

for handler in upload_handlers: 

    try: 
     handler.new_file(field_name, file_name, 
         content_type, content_length, charset) 
    except StopFutureHandlers: 
     break 

for i, handler in enumerate(upload_handlers): 
    while True: 
     chunk = request.read(handler.chunk_size) 
     if chunk: 

      handler.receive_data_chunk(chunk, counters[i]) 
      counters[i] += len(chunk) 
     else: 
      # no chunk 
      break 

for i, handler in enumerate(upload_handlers): 
    file_obj = handler.file_complete(counters[i]) 
    if not file_obj: 
     # some indication this didn't work? 
     return HttpResponse(status=500) 
    else: 
     # handle file obj! 
+0

+1 감사합니다. 내가 직장에 돌아 왔을 때 나는 그것을 줄 것이다. 나는 그 결과를보고 할 것이다. –

+0

그것은 매력처럼 작동했고, 나는 하루의 끝에 당신의 대답으로 그것을 편집 할 것입니다. 요청한대로 –

+1

을 입력하고 대답에 대한 코드를 편집했습니다. 개선 된 점이 있다면 자유롭게 편집 할 수 있습니다. 게시물에서 파생 된 작업으로 내 질문에 답변하고 싶지 않습니다. –

관련 문제