通过 POST httprequest 传输文件到 hololens(错误 429)
Transfer file to hololens via POST httprequest (error 429)
我在桌面应用程序上工作,通过 httprequest 与 hololens 眼镜通信(hololens 通过 USB 连接),特别是,我想要读取和写入文件。为此,我使用以下代码:
class BuildDeployPortal
{
// Consts
public static readonly string API_FileQuery = @"http://{0}/api/filesystem/apps/file";
// Classes & Structs
public struct ConnectInfo
{
public ConnectInfo(string ip, string user, string password)
{
IP = ip;
User = user;
Password = password;
}
public string IP;
public string User;
public string Password;
}
public async static void GetFile(ConnectInfo connectInfo, string knownfolderid, string filename, string packagefullname, string path)
{
try
{
// Query
string query = string.Format(API_FileQuery, connectInfo.IP);
query += "?knownfolderid=" + Uri.EscapeUriString(knownfolderid);
query += "&filename=" + Uri.EscapeUriString(filename);
query += "&packagefullname=" + Uri.EscapeUriString(packagefullname);
query += "&path=" + Uri.EscapeUriString(path);
// Create http request
var httpRequest = new HttpClient();
httpRequest.DefaultRequestHeaders.Clear();
httpRequest.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic",
EncodeTo64(connectInfo.User + ":" + connectInfo.Password));
HttpResponseMessage resp = await httpRequest.GetAsync(query);
byte[] responseMessage = await resp.Content.ReadAsByteArrayAsync();
}
catch (Exception ex)
{
//log some problems
}
}
public async static void UploadFile(ConnectInfo connectInfo, string knownfolderid, string packagefullname, string path, string filePath)
{
try
{
// Query
string query = string.Format(API_FileQuery, connectInfo.IP);
query += "?knownfolderid=" + Uri.EscapeUriString(knownfolderid);
query += "&packagefullname=" + Uri.EscapeUriString(packagefullname);
query += "&path=" + Uri.EscapeUriString(path);
// Create http request
var httpRequest = new HttpClient();
httpRequest.DefaultRequestHeaders.Clear();
httpRequest.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic",
EncodeTo64(connectInfo.User + ":" + connectInfo.Password));
byte[] data = File.ReadAllBytes(filePath);
ByteArrayContent byteContent = new ByteArrayContent(data);
HttpResponseMessage resp = await httpRequest.PostAsync(query, byteContent);
var responseMessage = await resp.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
//log some problems
}
}
// Helpers
private static string EncodeTo64(string toEncode)
{
byte[] toEncodeAsBytes = Encoding.ASCII.GetBytes(toEncode);
string returnValue = Convert.ToBase64String(toEncodeAsBytes);
return returnValue;
}
}
我给运行打电话
BuildDeployPortal.ConnectInfo co = new BuildDeployPortal.ConnectInfo("127.0.0.1:10080", "user", "pass");
//get file
BuildDeployPortal.GetFile(co, "LocalAppData", "2017-01-25-09-04-21_TestFile.csv", "app_full_package_name", "\LocalState");
//upload file
BuildDeployPortal.UploadFile(co, "LocalAppData", "app_full_package_name", "\LocalState",
"C:\Users\myuser\Desktop\info.json");
GetFile 方法工作正常并为我提供了我想要的文件,但 UploadFile 方法为我提供了以下响应:StatusCode:429,ReasonPhrase:'Too Many Requests'。如果我在执行期间只 运行 其中一个,结果相同。
我真的不明白为什么会出现这个错误,我只发送了 1 个请求(或者我遗漏了什么)。
有关信息,可以找到 REST API 定义 there。
谢谢
我并没有真正回答我的问题(为什么会出现此错误 429),但我提供了上传文件的替代解决方案。
我下载了 this solution 以获取 WindowsDevicePortalWrapper 库。
然后我调用以下代码上传文件:
IDevicePortalConnection connection = new DefaultDevicePortalConnection("http://127.0.0.1:10080", "user", "password");
DevicePortal dp = new DevicePortal(connection);
//write file
await dp.UploadFileAsync("LocalAppData", "C:\Users\ng1dd32\Desktop\info.json", "\LocalState", "Lexington_1.0.0.0_x86__ph1m9x8skttmg");
调试问题
问题是Chrome62发送了空的content-type
header,而Chrome61did发送了content-type
header 正确为 multipart/form-data
.
的原因是微软自己的FileExplorer.js代码用contentType: ""
调用了jQuery.ajax(...)
,所以留给浏览器解释一个空字符串。
摘自FileExplorer.js
$.ajax({url:"/api/filesystem/apps/file?"+$.param(r),cache:!1,contentType:"",processData:!1,data:n,type:"post"}).done(...)
REST API definition you linked from Microsoft 未能指定任何请求 Headers,也未能指定请求 Body,因此没有规范可用来确定正确的 [= =61=] 和 Body.
FWIW,微软自己的 C# Device Portal REST API wrapper 总是发送 content-type header:
string contentType = string.Format("multipart/form-data; boundary={0}", boundaryString);
解决方案
我创建了一个小书签,可以在设备门户的 FileExplorer 页面上正确上传文件。
扩展版
(function(jQuery) {
var pathLinkData = jQuery(".pathLink:last-child").data(),
path = pathLinkData.path,
packagename = pathLinkData.packagename,
knownfolderid = pathLinkData.knownfolderid,
url = '/api/filesystem/apps/file?knownfolderid=' + knownfolderid + '&packagefullname=' + packagename + '&path=%5C%5C' + path,
file_data = jQuery('#fileToUpload')[0].files[0],
form_data = new FormData();
form_data.append('file', file_data, file_data.name);
jQuery.ajax({
url: url,
dataType: 'text',
cache: false,
contentType: false,
processData: false,
data: form_data,
type: 'POST',
error: function(xhr, textStatus, error) { console.error(error) },
success: function(res){
alert('uploaded');
console.log(res);
}
});
})(jQuery);
紧凑型可复制小书签版本(也适用于 inspector/debugger 控制台)
javascript:(function(jQuery){var pathLinkData=jQuery('.pathLink:last-child').data(),path=pathLinkData.path,packagename=pathLinkData.packagename,knownfolderid=pathLinkData.knownfolderid,url='/api/filesystem/apps/file?knownfolderid='+knownfolderid+'&packagefullname='+packagename+'&path=%5C%5C'+path,file_data=jQuery('#fileToUpload')[0].files[0],form_data=new FormData();form_data.append('file',file_data,file_data.name);jQuery.ajax({url:url,dataType:'text',cache:false,contentType:false,processData:false,data:form_data,type:'POST',error:function(xhr,textStatus,error){console.error(error)},success:function(res){alert("uploaded");console.log(res);}});})(jQuery);
(我已经为 Hololens 开发了一年多,并在我的每台计算机分别升级到 Chrome 62 时发现了这一点。)
我在桌面应用程序上工作,通过 httprequest 与 hololens 眼镜通信(hololens 通过 USB 连接),特别是,我想要读取和写入文件。为此,我使用以下代码:
class BuildDeployPortal
{
// Consts
public static readonly string API_FileQuery = @"http://{0}/api/filesystem/apps/file";
// Classes & Structs
public struct ConnectInfo
{
public ConnectInfo(string ip, string user, string password)
{
IP = ip;
User = user;
Password = password;
}
public string IP;
public string User;
public string Password;
}
public async static void GetFile(ConnectInfo connectInfo, string knownfolderid, string filename, string packagefullname, string path)
{
try
{
// Query
string query = string.Format(API_FileQuery, connectInfo.IP);
query += "?knownfolderid=" + Uri.EscapeUriString(knownfolderid);
query += "&filename=" + Uri.EscapeUriString(filename);
query += "&packagefullname=" + Uri.EscapeUriString(packagefullname);
query += "&path=" + Uri.EscapeUriString(path);
// Create http request
var httpRequest = new HttpClient();
httpRequest.DefaultRequestHeaders.Clear();
httpRequest.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic",
EncodeTo64(connectInfo.User + ":" + connectInfo.Password));
HttpResponseMessage resp = await httpRequest.GetAsync(query);
byte[] responseMessage = await resp.Content.ReadAsByteArrayAsync();
}
catch (Exception ex)
{
//log some problems
}
}
public async static void UploadFile(ConnectInfo connectInfo, string knownfolderid, string packagefullname, string path, string filePath)
{
try
{
// Query
string query = string.Format(API_FileQuery, connectInfo.IP);
query += "?knownfolderid=" + Uri.EscapeUriString(knownfolderid);
query += "&packagefullname=" + Uri.EscapeUriString(packagefullname);
query += "&path=" + Uri.EscapeUriString(path);
// Create http request
var httpRequest = new HttpClient();
httpRequest.DefaultRequestHeaders.Clear();
httpRequest.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic",
EncodeTo64(connectInfo.User + ":" + connectInfo.Password));
byte[] data = File.ReadAllBytes(filePath);
ByteArrayContent byteContent = new ByteArrayContent(data);
HttpResponseMessage resp = await httpRequest.PostAsync(query, byteContent);
var responseMessage = await resp.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
//log some problems
}
}
// Helpers
private static string EncodeTo64(string toEncode)
{
byte[] toEncodeAsBytes = Encoding.ASCII.GetBytes(toEncode);
string returnValue = Convert.ToBase64String(toEncodeAsBytes);
return returnValue;
}
}
我给运行打电话
BuildDeployPortal.ConnectInfo co = new BuildDeployPortal.ConnectInfo("127.0.0.1:10080", "user", "pass");
//get file
BuildDeployPortal.GetFile(co, "LocalAppData", "2017-01-25-09-04-21_TestFile.csv", "app_full_package_name", "\LocalState");
//upload file
BuildDeployPortal.UploadFile(co, "LocalAppData", "app_full_package_name", "\LocalState",
"C:\Users\myuser\Desktop\info.json");
GetFile 方法工作正常并为我提供了我想要的文件,但 UploadFile 方法为我提供了以下响应:StatusCode:429,ReasonPhrase:'Too Many Requests'。如果我在执行期间只 运行 其中一个,结果相同。
我真的不明白为什么会出现这个错误,我只发送了 1 个请求(或者我遗漏了什么)。
有关信息,可以找到 REST API 定义 there。
谢谢
我并没有真正回答我的问题(为什么会出现此错误 429),但我提供了上传文件的替代解决方案。
我下载了 this solution 以获取 WindowsDevicePortalWrapper 库。 然后我调用以下代码上传文件:
IDevicePortalConnection connection = new DefaultDevicePortalConnection("http://127.0.0.1:10080", "user", "password");
DevicePortal dp = new DevicePortal(connection);
//write file
await dp.UploadFileAsync("LocalAppData", "C:\Users\ng1dd32\Desktop\info.json", "\LocalState", "Lexington_1.0.0.0_x86__ph1m9x8skttmg");
调试问题
问题是Chrome62发送了空的content-type
header,而Chrome61did发送了content-type
header 正确为 multipart/form-data
.
的原因是微软自己的FileExplorer.js代码用contentType: ""
调用了jQuery.ajax(...)
,所以留给浏览器解释一个空字符串。
摘自FileExplorer.js
$.ajax({url:"/api/filesystem/apps/file?"+$.param(r),cache:!1,contentType:"",processData:!1,data:n,type:"post"}).done(...)
REST API definition you linked from Microsoft 未能指定任何请求 Headers,也未能指定请求 Body,因此没有规范可用来确定正确的 [= =61=] 和 Body.
FWIW,微软自己的 C# Device Portal REST API wrapper 总是发送 content-type header:
string contentType = string.Format("multipart/form-data; boundary={0}", boundaryString);
解决方案
我创建了一个小书签,可以在设备门户的 FileExplorer 页面上正确上传文件。
扩展版
(function(jQuery) {
var pathLinkData = jQuery(".pathLink:last-child").data(),
path = pathLinkData.path,
packagename = pathLinkData.packagename,
knownfolderid = pathLinkData.knownfolderid,
url = '/api/filesystem/apps/file?knownfolderid=' + knownfolderid + '&packagefullname=' + packagename + '&path=%5C%5C' + path,
file_data = jQuery('#fileToUpload')[0].files[0],
form_data = new FormData();
form_data.append('file', file_data, file_data.name);
jQuery.ajax({
url: url,
dataType: 'text',
cache: false,
contentType: false,
processData: false,
data: form_data,
type: 'POST',
error: function(xhr, textStatus, error) { console.error(error) },
success: function(res){
alert('uploaded');
console.log(res);
}
});
})(jQuery);
紧凑型可复制小书签版本(也适用于 inspector/debugger 控制台)
javascript:(function(jQuery){var pathLinkData=jQuery('.pathLink:last-child').data(),path=pathLinkData.path,packagename=pathLinkData.packagename,knownfolderid=pathLinkData.knownfolderid,url='/api/filesystem/apps/file?knownfolderid='+knownfolderid+'&packagefullname='+packagename+'&path=%5C%5C'+path,file_data=jQuery('#fileToUpload')[0].files[0],form_data=new FormData();form_data.append('file',file_data,file_data.name);jQuery.ajax({url:url,dataType:'text',cache:false,contentType:false,processData:false,data:form_data,type:'POST',error:function(xhr,textStatus,error){console.error(error)},success:function(res){alert("uploaded");console.log(res);}});})(jQuery);
(我已经为 Hololens 开发了一年多,并在我的每台计算机分别升级到 Chrome 62 时发现了这一点。)