如何使用multipart/form-data格式上传文件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用multipart/form-data格式上传文件相关的知识,希望对你有一定的参考价值。

在网络编程过程中需要向服务器上传文件。Multipart/form-data是上传文件的一种方式。

Multipart/form-data其实就是浏览器用表单上传文件的方式。最常见的情境是:在写邮件时,向邮件后添加附件,附件通常使用表单添加,也就是用multipart/form-data格式上传到服务器。


表单形式上传附件

具体的步骤是怎样的呢?

首先,客户端和服务器建立连接(TCP协议)。

第二,客户端可以向服务器端发送数据。因为上传文件实质上也是向服务器端发送请求。

第三,客户端按照符合“multipart/form-data”的格式向服务器端发送数据。

 

既然Multipart/form-data格式就是浏览器用表单提交数据的格式,我们就来看看文件经过浏览器编码后是什么样子。

点击“Browse…”分别选择“unknow.gif”和“unknow1.gif”文件,点击“submit”按纽后,文件将被上传到服务器。

下面是服务器收到的数据:


服务器收到的数据

这是一个POST请求。所以数据是放在请求体内,而不是请求头内。

这行指出这个请求是“multipart/form-data”格式的,且“boundary”是 “---------------------------7db15a14291cce”这个字符串。

不难想象,“boundary”是用来隔开表单中不同部分数据的。例子中的表单就有 2 部分数据,用“boundary”隔开。“boundary”一般由系统随机产生,但也可以简单的用“-------------”来代替。

实际上,每部分数据的开头都是由"--" + boundary开始,而不是由 boundary 开始。仔细看才能发现下面的开头这段字符串实际上要比 boundary 多了个 “--”   

紧接着 boundary 的是该部分数据的描述。

接下来才是数据。


 

“GIF”gif格式图片的文件头,可见,unknow1.gif确实是gif格式图片。

在请求的最后,则是 "--" + boundary + "--" 表明表单的结束。

 

需要注意的是,在html协议中,用 “\\r\\n” 换行,而不是 “\\n”。

下面的代码片断演示如何构造multipart/form-data格式数据,并上传图片到服务器。

//---------------------------------------

// this is the demo code of using multipart/form-data to upload text and photos.

// -use WinInet APIs.

//

//

// connection handlers.

//

HRESULT hr;

HINTERNET m_hOpen;

HINTERNET m_hConnect;

HINTERNET m_hRequest;

//

// make connection.

//

...

//

// form the content.

//

std::wstring strBoundary = std::wstring(L"------------------");

std::wstring wstrHeader(L"Content-Type: multipart/form-data, boundary=");

wstrHeader += strBoundary;

HttpAddRequestHeaders(m_hRequest, wstrHeader.c_str(), DWORD(wstrHeader.size()), HTTP_ADDREQ_FLAG_ADD);

//

// "std::wstring strPhotoPath" is the name of photo to upload.

//

//

// uploaded photo form-part begin.

//

std::wstring strMultipartFirst(L"--");

strMultipartFirst += strBoundary;

strMultipartFirst += L"\\r\\nContent-Disposition: form-data; name=\\"pic\\"; filename=";

strMultipartFirst += L"\\"" + strPhotoPath + L"\\"";

strMultipartFirst += L"\\r\\nContent-Type: image/jpeg\\r\\n\\r\\n";

//

// "std::wstring strTextContent" is the text to uploaded.

//

//

// uploaded text form-part begin.

//

std::wstring strMultipartInter(L"\\r\\n--");

strMultipartInter += strBoundary;

strMultipartInter += L"\\r\\nContent-Disposition: form-data; name=\\"status\\"\\r\\n\\r\\n";

std::wstring wstrPostDataUrlEncode(CEncodeTool::Encode_Url(strTextContent));

// add text content to send.

strMultipartInter += wstrPostDataUrlEncode;

std::wstring strMultipartEnd(L"\\r\\n--");

strMultipartEnd += strBoundary;

strMultipartEnd += L"--\\r\\n";

//

// open photo file.

//

// ws2s(std::wstring)

// -transform "strPhotopath" from unicode to ansi.

std::ifstream *pstdofsPicInput = new std::ifstream;

pstdofsPicInput->open((ws2s(strPhotoPath)).c_str(), std::ios::binary|std::ios::in);

pstdofsPicInput->seekg(0, std::ios::end);

int nFileSize = pstdofsPicInput->tellg();

if(nPicFileLen == 0)

return E_ACCESSDENIED;

char *pchPicFileBuf = NULL;

try

pchPicFileBuf = new char[nPicFileLen];

catch(std::bad_alloc)

hr = E_FAIL;

if(FAILED(hr))

return hr;

pstdofsPicInput->seekg(0, std::ios::beg);

pstdofsPicInput->read(pchPicFileBuf, nPicFileLen);

if(pstdofsPicInput->bad())

pstdofsPicInput->close();

hr = E_FAIL;

delete pstdofsPicInput;

if(FAILED(hr))

return hr;

// Calculate the length of data to send.

std::string straMultipartFirst = CEncodeTool::ws2s(strMultipartFirst);

std::string straMultipartInter = CEncodeTool::ws2s(strMultipartInter);

std::string straMultipartEnd = CEncodeTool::ws2s(strMultipartEnd);

int cSendBufLen = straMultipartFirst.size() + nPicFileLen + straMultipartInter.size() + straMultipartEnd.size();

// Allocate the buffer to temporary store the data to send.

PCHAR pchSendBuf = new CHAR[cSendBufLen];

memcpy(pchSendBuf, straMultipartFirst.c_str(), straMultipartFirst.size());

memcpy(pchSendBuf + straMultipartFirst.size(), (const char *)pchPicFileBuf, nPicFileLen);

memcpy(pchSendBuf + straMultipartFirst.size() + nPicFileLen, straMultipartInter.c_str(), straMultipartInter.size());

memcpy(pchSendBuf + straMultipartFirst.size() + nPicFileLen + straMultipartInter.size(), straMultipartEnd.c_str(), straMultipartEnd.size());

//

// send the request data.

//

HttpSendRequest(m_hRequest, NULL, 0, (LPVOID)pchSendBuf, cSendBufLen)

参考技术A 您好,很高兴能帮助您,
在网络编程过程中需要向服务器上传文件。Multipart/form-data是上传文件的一种方式。
Multipart/form-data其实就是浏览器用表单上传文件的方式。最常见的情境是:在写邮件时,向邮件后添加附件,附件通常使用表单添加,也就是用multipart/form-data格式上传到服务器。

表单形式上传附件
具体的步骤是怎样的呢?
首先,客户端和服务器建立连接(TCP协议)。
第二,客户端可以向服务器端发送数据。因为上传文件实质上也是向服务器端发送请求。
第三,客户端按照符合“multipart/form-data”的格式向服务器端发送数据。

Multipart/form-data格式就是浏览器用表单提交数据的格式
你的采纳是我前进的动力,还有不懂的地方,请你继续“追问”!
如你还有别的问题,可另外向我求助;答题不易,互相理解,互相帮助!
参考技术B 首先,客户端和服务器建立连接(TCP协议)。
第二,客户端可以向服务器端发送数据。因为上传文件实质上也是向服务器端发送请求。
第三,客户端按照符合“multipart/form-data”的格式向服务器端发送数据。

如何上传文件数组?

【中文标题】如何上传文件数组?【英文标题】:How to upload an array of files? 【发布时间】:2017-01-19 07:53:01 【问题描述】:

我试图通过多次提交表单来构建一个文件数组,然后将这些文件移动到一个目录,但它不起作用。每次上传都会覆盖前一次,然后它甚至不会移动那个(upload_to_file() 函数不做任何事情)

HTML:

<form id="form" action="home.php" method="post" enctype="multipart/form-data">
        <input type="hidden" name="MAX_FILE_SIZE" value="8000000">
        <input class="upload_btn" type="file" name="images[]" id="image_file">
        <input type="submit" id="img_submit" class="form_button" name="submit_image" value="upload"/>
</form>

重要的是只有一个上传按钮可用于上传多个文件。

我需要将它们存储在一个数组中,这样我就可以在任何地方循环显示它们的 ['name']。for ($i = 0; $i &lt; count($_FILES['images']['name']); $i++)

然后,一旦提交了另一个表单,它就会调用一个函数将数组中的每个文件移动到一个目录中。

包含的 php 文件中的函数:

function upload_to_file()
$image_paths = array();
$target_dir = "uploads/images/";
$path = $target_dir . basename($_FILES['images']['name'][0]);

if(isset($_FILES['images']['name'][0]) && $_FILES['images']['size'][0] > 0)

    if (move_uploaded_file($_FILES['images']['tmp_name'][0], $path)) 
        $image_paths[0] = "uploads/images/";
    

return $image_paths;


我只是用数组中的第一个元素来测试它,但以后需要做一个循环。

【问题讨论】:

这些上传作为单独的请求发送,php 不知道多个请求是更大的一部分。因此,要么您必须使用“会话”进行持久性,并从会话中收集跨越 所有 请求的信息,,更灵活,您需要在客户端上实现它侧,以便创建文件索引之类的内容并在客户端显示。这就是所谓的“上传管理器”。 我尝试使用类似 array_push($_SESSION['images'], $_FILES['image_file']);效果很好,直到 move_uploaded_file($_SESSION['images'][0]["tmp_name"], $path) 部分。即使我输出 $_SESSION['images'][0]["tmp_name"] 它仍然存在。 所以你是说你用一个表单上传文件并用另一个表单组织它们,但你不将它们存储在数据库中(路径等)? move_uploaded_file() 仅在请求处理本身期间起作用。所以当一个文件被上传时,不管你手动保存到会话变量中的什么。因此,要么完全处理每个上传的文件,要么,如果确实需要,将其移动到您手动处理的临时位置。不过,这将需要清理。不是一个好的架构。 好的,我尝试在提交表单后一个一个地移动文件,并且至少 move_uploaded_file() 有效。我不想这样做,因为现在如果我只想作为提交的文件删除它必须去删除它,而不是仅仅从数组中删除它。 【参考方案1】:

这是帮助您上传多个文件的程序。代码中的“sub”是提交按钮的名称。 “upload”是文件控制器的名称,上传的文件存储在您必须在根文件夹上创建的名为“img”的目录中。

<?php

if (isset($_POST['sub'])) 
    if (count($_FILES['upload']['name']) > 0) 
     for ($i=0; $i<count($_FILES['upload']['name']); $i++) 
      $tmpFilePath = $_FILES['upload']['tmp_name'][$i];
      if ($tmpFilePath != "") 
        $shortname = $_FILES['upload']['name'][$i];
        $filePath = "img/" . date('d-m-Y-H-i-s').'-'.$_FILES['upload']['name'][$i];
        if (move_uploaded_file($tmpFilePath, $filePath)) 
          $files[] = $shortname;                    
        
      
    
  
  echo "<h1>Uploaded:</h1>";    
  if(is_array($files))
    echo "<ul>";
    foreach($files as $file)
      echo "<li>$file</li>";
    
    echo "</ul>";
  

?>

给出HTML代码

<form action="" enctype="multipart/form-data" method="post">
    <input id='upload' name="upload[]" type="file" multiple="multiple" />
    <input type="submit" name="sub" value="Upload Now">
</form>

【讨论】:

以上是关于如何使用multipart/form-data格式上传文件的主要内容,如果未能解决你的问题,请参考以下文章

如何使用multipart/form-data格式上传文件

如何上传文件数组?

带文件的表单提交

文件上传和下载

如何利用java网络编程,发送multipart/form

如何通过HttpClient去POST一个multipart/form-data数据