如何通过javascript/php存储和发送$_FILES


how to store and send $_FILES via javascript/php

我正在尝试设置一个可以同时上传到YouTube和Vimeo的表单。我更喜欢使用Posterous.com做这样的事情,但自从他们被推特收购后,他们的帮助团队已经离开了地球,因为我的电子邮件现在都没有回复(他们已经删除了一堆服务)。。。

因此,无论如何,以下是youtube流程的工作方式:

  1. 为要通过网络表单上传的视频设置标题和类别
  2. 提交表单,从youtube获取访问令牌
  3. 生成另一个表单,允许您选择要上载的文件
  4. 发送提交表单、访问令牌和文件,youtube上传视频

我想做的是用拖放上传器将其转化为一个步骤:

  1. 将文件拖放到页面上
  2. javascript获取文件信息,将文件名设置为视频标题,并使用默认类别
  3. javascript调用php并发送filename&类别到youtube,获取访问令牌并使用文件输入创建表单
  4. 获得令牌后,发送一个POST(通过PHP)请求,其中文件上传存储在会话变量中(现在我必须再次选择文件并单击提交)

难道我不能使用第一步中的文件信息,将其存储在会话变量中,并通过php以编程方式提交令牌和文件信息吗?我不知道如何发送这些数据,就像它是作为表单提交时发送的一样,我也不总是从youtube收到回复代码来修复我的代码。

这可能是我需要的答案:通过curl发送xml和头,但我不知道如何设置$xmlString$videoData

编辑::

我想我需要通过PHP而不是javascript来完成这项工作,因为我正在尝试修改以下代码:

/**
 * Create upload form by sending the incoming video meta-data to youtube and
 * retrieving a new entry. Prints form HTML to page.
 *
 * @param string $VideoTitle The title for the video entry.
 * @param string $VideoDescription The description for the video entry.
 * @param string $VideoCategory The category for the video entry.
 * @param string $nextUrl (optional) The URL to redirect back to after form upload has completed.
 * @return void
 */
function createUploadForm($videoTitle, $videoCategory, $nextUrl = null) {
$httpClient = getAuthSubHttpClient();
$youTubeService = new Zend_Gdata_YouTube($httpClient);
$newVideoEntry = new Zend_Gdata_YouTube_VideoEntry();
$newVideoEntry->setVideoTitle($videoTitle);
//make sure first character in category is capitalized
$videoCategory = strtoupper(substr($videoCategory, 0, 1))
    . substr($videoCategory, 1);
$newVideoEntry->setVideoCategory($videoCategory);
// convert videoTags from whitespace separated into comma separated
$tokenHandlerUrl = 'https://gdata.youtube.com/action/GetUploadToken';
try {
    $tokenArray = $youTubeService->getFormUploadToken($newVideoEntry, $tokenHandlerUrl);
    if (loggingEnabled()) {
        logMessage($httpClient->getLastRequest(), 'request');
        logMessage($httpClient->getLastResponse()->getBody(), 'response');
    }
} catch (Zend_Gdata_App_HttpException $httpException) {
    print 'ERROR ' . $httpException->getMessage()
        . ' HTTP details<br /><textarea cols="100" rows="20">'
        . $httpException->getRawResponseBody()
        . '</textarea><br />'
        . '<a href="session_details.php">'
        . 'click here to view details of last request</a><br />';
    return;
} catch (Zend_Gdata_App_Exception $e) {
    print 'ERROR - Could not retrieve token for syndicated upload. '
        . $e->getMessage()
        . '<br /><a href="session_details.php">'
        . 'click here to view details of last request</a><br />';
    return;
}
$tokenValue = $tokenArray['token'];
$postUrl = $tokenArray['url'];
// place to redirect user after upload
if (!$nextUrl) {
    $nextUrl = $_SESSION['homeUrl'];
} 
//instead of echoing the form below, send $_FILES from previous form submit
print <<< END
    <br />      
    <form id="uploadToYouTubeForm" action="${postUrl}?nexturl=${nextUrl}" method="post" enctype="multipart/form-data">
    <input id="uploadToYouTube" name="file" type="file" />
    <input name="token" type="hidden" value="${tokenValue}"/>
    <input value="Upload Video File" type="submit" />
    </form>
END;
}

我想我可能有一个解决方案。以下是我想使用的表格:

<!--removing the action ensures form will be sent via javascript, then when that request comes back, I can add in the authenticated youtube URL needed-->
<form id="upload" action="" method="POST" enctype="multipart/form-data" class="form-horizontal"><!--upload.php-->
  <fieldset>
    <legend><h1>Video File Upload</h1></legend>
    <input type="hidden" id="MAX_FILE_SIZE" name="MAX_FILE_SIZE" value="1000000000" /> <!--1GB-->
    <p id="filedrag">Drag and drop a video file from your computer here. Or use the 'File upload' button below.</p><!--dragFileHere-->
    <label class="control-label" for="fileselect">Files to upload:</label>
    <input type="file" id="fileselect" name="fileselect[]" /> <!--multiple="multiple"-->
    <button class="btn" id="submitbutton" type="submit">Upload Files</button> <!--hidden via js/css-->
    <div class="progress progress-striped active">
      <div class="bar" style="width: 0%;"></div>
    </div>
    <label class="hide" for="video-title">Title</label>
    <input type="text" id="video-title" class="span4" placeholder="Video Title"/>
    <label class="control-label" for="video-category">Category</label>
    <select id="video-category" name="videoCategory" class="span4">
      <option value="Autos">Autos &amp; Vehicles</option>
      <option value="Music">Music</option>
      .......
      <option value="Entertainment" selected>Entertainment</option>
    </select>
    <input id="token" type="text" placeholder="token"/> <!--will be hidden-->
</div>
</fieldset>
</form>

通过将action属性留空(并使用我已经准备好的脚本来响应用户交互),我可以确保表单是通过javascript:以编程方式提交的

<script src=http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js></script>
<script src=_js/video_app.js" type="text/javascript></script>
<script>
  /*
  filedrag.js - HTML5 File Drag & Drop demonstration
  Featured on SitePoint.com
  Developed by Craig Buckler (@craigbuckler) of OptimalWorks.net (without jQuery)
  */
  // output information
  function Output(msg) {
    $('#messages').html(msg + $('#messages').html());
  }
  // file drag hover
  function FileDragHover(e) {
    e.stopPropagation();
    e.preventDefault();
    $(this).addClass("hover");
  } 
  function FileDragOut(e) {
    $(this).removeClass("hover");
  }
  // file selection
  function FileSelectHandler(e) {
    // cancel event and hover styling
    FileDragHover(e);
    // fetch FileList object
    var files = e.target.files || e.dataTransfer.files;
    // process all File objects
    for (var i = 0, f; f = files[i]; i++) {
      ParseFile(f); //prints file data to div and optionally displays content of selected file
      UploadFile(f); //uploads file to server
    }
  }
  // output file information
  function ParseFile(file) {
    videoName = file.name;
    videoType = file.type;
    videoURL = "http://localhost/"+videoName;
    videoCategory = $('#video-category').val();
    Output(
      "</strong> type: <strong>" + file.type +
      "</strong> size: <strong>" + file.size +
      "</strong> bytes</p>"
    );
    // sets a default value because a title is needed for youtube to send response  
    if( $('#video-title').val() == $('#video-title').attr('placeholder') ) {
      $('#video-title').val(videoName);
    }
    var reader = new FileReader();
    reader.onload = function(e) {
      var fileContents = e.target.result;
      Output(
        '<img src="'+e.target.result+'"/>'
      );
    }
    reader.readAsDataURL(file);
    //get upload token
    ytVideoApp.prepareSyndicatedUpload(videoName, videoCategory);
  }
  // upload video files
  function UploadFile(file) {       
    var xhr = new XMLHttpRequest();
    if (xhr.upload && file.size <= $('#MAX_FILE_SIZE').val()) { //&& file.type == "video/mp4" or video/*
      xhr.upload.addEventListener("progress", function(e) {
        var pc = Math.ceil(e.loaded / e.total * 100);
      }, false);
      // file received/failed
      xhr.onreadystatechange = function(e) {
        if (xhr.readyState == 4) {
          if(xhr.status == 200) { //success
          } else { //fail  
          }
        }
      };
      // start upload
      xhr.open("POST", 'upload.php', true); //$("#upload").attr('action')
      xhr.setRequestHeader("X_FILENAME", file.name);
      xhr.send(file);
    }
  }
  // initialize
  function Init() {
    // file select
    $('#fileselect').change(FileSelectHandler);
    // is XHR2 available?
    var xhr = new XMLHttpRequest();
    if (xhr.upload) {
      // file drop
      $('#filedrag').bind('dragover', FileDragHover);
      $('#filedrag').bind('dragleave', FileDragOut);
      //I can't get the below line to work, so I've used the ugly fallback
      //$('#filedrag').bind('drop', FileSelectHandler);
      document.getElementById('filedrag').addEventListener("drop", FileSelectHandler, false);           
      filedrag.style.display = "block";
      // remove submit button
      submitbutton.style.display = "none";
    }
  }
  // call initialization file
  if (window.File && window.FileList && window.FileReader) {
    Init();
  }
</script>

_js/video_app.js:

/**
* Zend Framework
* @package    Zend_Gdata
....
/**
* provides namespacing for the YouTube Video Application PHP version (ytVideoApp)
**/
var ytVideoApp = {};
/**
* Sends an AJAX request to the server to retrieve a list of videos or
* the video player/metadata.  Sends the request to the specified filePath
* on the same host, passing the specified params, and filling the specified
* resultDivName with the resutls upon success.
* @param {String} filePath The path to which the request should be sent
* @param {String} params The URL encoded POST params
* @param {String} resultDivName The name of the DIV used to hold the results
*/
ytVideoApp.sendRequest = function(filePath, params, resultDivName) {
  if (window.XMLHttpRequest) {
    var xmlhr = new XMLHttpRequest();
  } else {
    var xmlhr = new ActiveXObject('MSXML2.XMLHTTP.3.0');
  }
  xmlhr.open('POST', filePath);
  xmlhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 
  xmlhr.onreadystatechange = function() {
    var resultDiv = document.getElementById(resultDivName);
    if (xmlhr.readyState == 1) {
      resultDiv.innerHTML = '<b>Loading...</b>'; 
    } else if (xmlhr.readyState == 4 && xmlhr.status == 200) {
      if (xmlhr.responseText) {
        resultDiv.innerHTML = xmlhr.responseText;
      }
    } else if (xmlhr.readyState == 4) {
      alert('Invalid response received - Status: ' + xmlhr.status);
    }
  }
  xmlhr.send(params);
}

ytVideoApp.prepareSyndicatedUpload = function(videoTitle, videoCategory, fileContents) {    
  var filePath = '_scripts/operations.php';
  var params = 'operation=create_upload_form' +
    '&videoTitle=' + videoTitle +
    '&videoCategory=' + videoCategory;
  ytVideoApp.sendRequest(filePath, params, ytVideoApp.SYNDICATED_UPLOAD_DIV);
}

_脚本/操作.pp:

require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_YouTube');
Zend_Loader::loadClass('Zend_Gdata_AuthSub');
Zend_Loader::loadClass('Zend_Gdata_App_Exception');
/*
 * The main controller logic.
 *
 * POST used for all authenticated requests
 * otherwise use GET for retrieve and supplementary values
 */
session_start();
setLogging('on');
generateUrlInformation();
if (!isset($_POST['operation'])) {
  // if a GET variable is set then process the token upgrade
  if (isset($_GET['token'])) {
    updateAuthSubToken($_GET['token']);
  } else {
    if (loggingEnabled()) {
      logMessage('reached operations.php without $_POST or $_GET variables set', 'error');
      header('Location: add-content.php');
    }
  }
}
$operation = $_POST['operation'];
switch ($operation) {
  ....
  case 'create_upload_form':
    createUploadForm($_POST['videoTitle'],
                     $_POST['videoCategory'],
                     $_POST['videoContents']);
  break;
  ....
  default:
    unsupportedOperation($_POST);
  break;
}
function createUploadForm($videoTitle, $videoCategory, $nextUrl = null) {
  $httpClient = getAuthSubHttpClient();
  $youTubeService = new Zend_Gdata_YouTube($httpClient);
  $newVideoEntry = new Zend_Gdata_YouTube_VideoEntry();
  $newVideoEntry->setVideoTitle($videoTitle);
  //make sure first character in category is capitalized
  $videoCategory = strtoupper(substr($videoCategory, 0, 1))
                              . substr($videoCategory, 1);
  $newVideoEntry->setVideoCategory($videoCategory);
  // convert videoTags from whitespace separated into comma separated
  $tokenHandlerUrl = 'https://gdata.youtube.com/action/GetUploadToken';
  try {
    $tokenArray = $youTubeService->getFormUploadToken($newVideoEntry, $tokenHandlerUrl);
    if (loggingEnabled()) {
      logMessage($httpClient->getLastRequest(), 'request');
      logMessage($httpClient->getLastResponse()->getBody(), 'response');
    }
  } catch (Zend_Gdata_App_HttpException $httpException) {
    print 'ERROR ' . $httpException->getMessage()
                   . ' HTTP details<br /><textarea cols="100" rows="20">'
                   . $httpException->getRawResponseBody()
                   . '</textarea><br />'
                   . '<a href="session_details.php">'
                   . 'click here to view details of last request</a><br />';
    return;
  } catch (Zend_Gdata_App_Exception $e) {
    print 'ERROR - Could not retrieve token for syndicated upload. '
                 . $e->getMessage()
                 . '<br /><a href="session_details.php">'
                 . 'click here to view details of last request</a><br />';
    return;
  }
  $tokenValue = $tokenArray['token'];
  $postUrl = $tokenArray['url'];
  // place to redirect user after upload
  if (!$nextUrl) {
    $nextUrl = $_SESSION['homeUrl'];
  }
  //instead of outputting the form below, send variables (json???) to be interpreted by xmlhr in _js/video_app.js
  //print <<< END
  //<br />      
  //<p>url: ${postUrl}?nexturl=${nextUrl}</p>
  //<form id="uploadToYouTubeForm" action="temp.php" method="post" enctype="multipart/form-data">
  //<input id="uploadToYouTube" name="file" type="file" onchange="autoUploadToYouTube();" /><br/>
  //token: <input id="token" name="token" type="text" value="${tokenValue}"/><br/>
  //<input value="Manual upload" type="submit" />
  //</form>     
  //END;
  //}

因此,第一个javascript监听拖拽&drop(在div上)或change()事件。表单实际上并没有提交,而是收集数据并通过ajax发送到php脚本,php脚本返回上传所需的令牌和url。然后,原始PHP脚本输出一个表单来选择文件(以及一个包含令牌的隐藏字段)。除此之外,我想将url和令牌作为变量传递,并使用XMLHttpRequest将这些变量放在表单的action属性中,并将键设置为隐藏输入字段的值。然后可以通过$('#uploadToYouTubeForm').submit(); 提交字段

唯一可能出现的问题是向youtube应用程序发送额外信息,我希望它会忽略它,或者我可能需要以编程方式删除youtube不接受的字段(标题、类别、最大文件大小)。。。。