I'm trying to send an image to my NodeJS API from Unity but unfortunately I can't make it work. Here is an extract of my c# code:
private string POSTUrl = "http://myNodeJSAPI.com/upload-image";
private Dictionary<string, string> postHeader = new Dictionary<string, string>();
public WWW POST() {
WWW www;
postHeader["content-Type"] = "application/octet-stream";
postData = File.ReadAllBytes("Assets/Resources/ImageIWantToSend.png");
www = new WWW(POSTUrl, postData, postHeader);
StartCoroutine(WaitForRequest(www));
return www;
}
IEnumerator WaitForRequest(WWW www)
{
yield return www;
if (www.error == null)
{
Debug.Log("WWW Ok!: " + www.text);
}
else
{
Debug.Log("WWW Error: " + www.error);
}
}
and here the relevant part of my nodejs one:
app.post('/upload-image', rawBody, function (req, res) {
console.log("File received!");
if (req.rawBody && req.bodyLength > 0) {
fs.writeFile("/tmp/ImageIWantToSend.png", req.rawBody, function (err) {
if (err) {
return console.log(err);
}
console.log("Image has been saved! Starting other things...");
})
}
}
The request is sent but never received by the API (I get 503 bad gateway). I try the same request outside Unity and it seems to work (using Postman: basic post request with image attached as binary).
Did I make a mistake, or is there another way to achieve this?
Thanks!
EDIT: I finally solved the problem by changing application/octet-stream to text/html. If there is a better way to send data to Nodejs I'm still interested
Check out Unity's new UnityWebRequest class.It is a replacement for original WWW.
You can also use C# WebClient or HttpWebRequest class. They work with Unity if you are not using WebGL build.
This is also a good unity plugin to handle requests, though it is not free.
Related
I am using UnityWebRequest to update (PUT) Raw JSON on the server. Unfortunately I get the error HTTP/1.1 405 Method Not Allowed. I am assuming that I am not Encoding it correctly. What could be the issue here?
This is a follow up question asked by my colleague: HTTP Method Not Allowed in REST API Post
We have tried to encode it in different ways as given below in the script. Unfortunately it does not work.
We have this JSON online and to enter data (PUT), it is a must to have the name and address fields as non-empty.
IEnumerator Post()
{
byte[] myData = System.Text.Encoding.UTF8.GetBytes("{'name': 'User01', 'address':{'raw':'MountFiji'}}");
UnityWebRequest www = UnityWebRequest.Put("website_of_the_user", myData);
www.SetRequestHeader("Content-Type", "application/json");
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError) {
Debug.Log(www.error);
}
else {
Debug.Log("Upload complete!");
}
}
{
"name": "",
"address": {
"raw": ""
}
}
The webpage where we have our API has following properties:
HTTP 200 OK
Allow: GET, HEAD, PUT, OPTIONS, DELETE
Content-Type: application/json
I got it working. If anyone in future faces this problem, then the solution is simpler than you think. The problem was the JSON format code, it is important to give \ since the data is a string. Here is the complete code:
UnityWebRequest www = UnityWebRequest.Put(URL_01, "{\"name\":\"user_01\",\"address\":{\"raw\":\"MountFiji\"}}");
www.SetRequestHeader ("Content-Type", "application/json");
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Form upload complete!");
}
I'm a beginner in Unity3D; i have to develop a mobile app and i need to manage user profile data; i have to communicate these data with server using REST services.
Everything works fine when i send Json (eg name, email, phone number, etc.) from my app, but I can't update the profile picture.
What i need is:
Content-Type = multipart/form-data
key="profile_picture", value=file_to_upload (not the path)
I read a lot about networking in Unity and tried different combinations of UnityWebRequest, List, WWWform but nothing seems to work for this kind of PUT service.
UnityWebRequest www = new UnityWebRequest(URL + user.email, "PUT");
www.SetRequestHeader("Content-Type", "multipart/form-data");
www.SetRequestHeader("AUTHORIZATION", authorization);
//i think here i'm missing the correct way to set up the content
I can correctly simulate the update from Postman, so it's not a problem with server; i'm pretty sure that the problem is that i can't convert this logic inside the app.
Upload from Postman correctly working(1)
Upload from Postman correctly working(2)
Any kind of help and code suggestion will be appreciated.
Thanks
With Put you usually only send file data but without a form.
You can add a multipart form using UnityWebRequest.Post
IEnumerator Upload()
{
List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
formData.Add(new MultipartFormFileSection("profile_picture", byte[], "example.png", "image/png"));
UnityWebRequest www = UnityWebRequest.Post(url, formData);
// change the method name
www.method = "PUT";
yield return www.SendWebRequest();
if(www.error)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Form upload complete!");
}
}
using a MultipartFormFileSection
Or alternatively you can use a WWWForm
IEnumerator Upload()
{
WWWForm form = new WWWForm();
form.AddBinaryData("profile_picture", bytes, "filename.png", "image/png");
// Upload via post request
var www = UnityWebRequest.Post(screenShotURL, form);
// change the method name
www.method = "PUT";
yield return www.SendWebRequest();
if (www.error)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Finished Uploading Screenshot");
}
}
using WWWForm.AddBinaryData
Note that for user authentication you have to encode your credentials properly:
string authenticate(string username, string password)
{
string auth = username + ":" + password;
auth = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(auth));
auth = "Basic " + auth;
return auth;
}
www.SetRequestHeader("AUTHORIZATION", authenticate("user", "password"));
(Source)
When Unity sends the POST request it doesn't pass the POST data, so the server returns an error (it gets the server response). I've seen that several people had a similar issue and it got fixed by adding www.chunkedTransfer = false;, however, that doesn't work for me.
I've also seen that some people use WWWForm instead of IMultipartFormSection, but I haven't tried it because it is deprecated.
I'm using PHP, but I've also tried it with Perl and it didn't work either. When I manually send a POST request everything works normally, so it seems the issue is in Unity. I'm new to Unity, so any help would be appreciated. I'm using the current latest version, 2018.2.18f1 Personal.
My code is pretty much the same as the official Unity documentation for sending POST request, but apparently it doesn't work. Here is my code:
C#:
public void Click() {
StartCoroutine(PostRequest("http://127.0.0.1/test.php", "help"));
}
IEnumerator PostRequest(string url, string data) {
List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
formData.Add(new MultipartFormDataSection("data=" + data));
UnityWebRequest www = UnityWebRequest.Post(url, formData);
www.chunkedTransfer = false;
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError) {
Debug.Log(www.error);
} else {
Debug.Log(www.downloadHandler.text);
}
}
PHP:
<?php echo "Server received: " . $_POST["data"]; ?>
Christoph Lütjen pointed out that according to this it should be new MultipartFormDataSection("data", data), despite the official documentation example using new MultipartFormDataSection("field1=foo&field2=bar").
Changing it to new MultipartFormDataSection("data", data) fixed the issue.
I am working on a project that requires to oauth2 token to communicate. The backend gives me a curl command, but I have no idea how to put it into WWW form in Unity because I have no former experience with http or json file. Could you help me to access the token? Thanks. Here how the curl code looks like:
$ curl -v -u {CLIENT_ID}:{CLIENT_SECRET} "https://api.domo.com/oauth/token?grant_type=client_credentials&scope={SCOPE}"
and here is an example:
$ curl -v -u 441e307a-b2a1-4a99-8561-174e5b153fsa:f103fc453d08bdh049edc9a1913e3f5266447a06d1d2751258c89771fbcc8087 "https://api.domo.com/oauth/token?grant_type=client_credentials&scope=data%20user"
Thank you so much!
To get oauth2 token with the WWW API, use the WWWForm to create form that contains the grant_type, client_id and client_secret. When you make the request, the token should be returned in Json format. Use Unity's JsonUtility to convert it to string you can easily obtain.
Retrieving Token:
[Serializable]
public class TokenClassName
{
public string access_token;
}
IEnumerator GetAccessToken()
{
var url = "http://yoururl.com";
var form = new WWWForm();
form.AddField("grant_type", "client_credentials");
form.AddField("client_id", "login-secret");
form.AddField("client_secret", "secretpassword");
WWW www = new WWW(url, form);
//wait for request to complete
yield return www;
//and check for errors
if (String.IsNullOrEmpty(www.error))
{
string resultContent = www.text;
UnityEngine.Debug.Log(resultContent);
TokenClassName json = JsonUtility.FromJson<TokenClassName>(resultContent);
UnityEngine.Debug.Log("Token: " + json.access_token);
}
else
{
//something wrong!
UnityEngine.Debug.Log("WWW Error: " + www.error);
}
}
Using the Token:
After you receive the token, it will be stored in the json.access_token variable. You can use then use that to make requests by adding it to the Header of your other WWW requests. That header is stored in a Dictionary like this:
headers.Add("Authorization", "Bearer " + token);
Here is an full example for making a request with the received token:
IEnumerator makeRequestWithToken(string token)
{
var url = "http://yoururl.com";
Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("Authorization", "Bearer " + token);
WWWForm formData = new WWWForm();
formData.AddField("YourField", "YourVal");
formData.AddField("YourField", "YourVal");
WWW www = new WWW(url, formData.data, headers);
yield return www;
//and check for errors
if (String.IsNullOrEmpty(www.error))
{
string resultContent = www.text;
UnityEngine.Debug.Log(resultContent);
}
else
{
//something wrong!
UnityEngine.Debug.Log("WWW Error: " + www.error);
}
}
Unity now uses UnityWebRequest. If possible start using that. Here is an example of retrieving oauth2 with UnityWebRequest.
Note that both functions are coroutine functions and must be started with the StartCoroutine function like StartCoroutine(GetAccessToken()); and StartCoroutine(makeRequestWithToken("YourToken"));.
I got it working using the following method:
Refer to Travis's fitbit code
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(_clientID + ":" + _consumerSecret);
var encoded = Convert.ToBase64String(plainTextBytes);
var form = new WWWForm();
form.AddField("client_id", _clientID);
form.AddField("grant_type", "authorization_code");
form.AddField("redirect_uri", WWW.UnEscapeURL(CallBackUrl));
form.AddField("code", _returnCode);
var headers = form.headers;
headers["Authorization"] = "Basic " + encoded;
_wwwRequest = new WWW("https://api.fitbit.com/oauth2/token", form.data, headers);
Relate to your backend, see what kind of oauth2 they are using, you can see the oauth2 documentation Here. Feel free to leave a comment for question.
I'm using WWW to interact with a RESTful web service. I have a problem sending XML files to the server through POST requests, though. This is my code:
if(Syste.IO.File.Exists(filePath)){
byte [] raw = File.ReadAllBytes(filePath);
WWWForm form = new WWWForm();
form.AddBinaryData("fileUpload", raw, "", "text/xml");
WWW www = new WWW(host + auth + "/behaviors", form);
StartCoroutine(myCoroutine(www));
}
IEnumerator myCoroutine(WWW www){
yield return www;
if (www.error == null)
{
Debug.Log("Text: " + www.text);
proceedToNextRequest = true;
} else {
Debug.Log("Error: "+ www.error);
Application.Quit();
}
}
The answer from the server is, though, "Unsupported Media Type", and I've no idea what's wrong. I generally use POSTMAN on Google Chrome to send these requests, and it works fine. Any tips?
I have found a solution for this: instead of using the WWW class(which, anyway, according to the documentation I'm pretty sure it can be used for this pupose), I'm using WebRequest. How this can be achieved is very well explained in the previous link and in this question: HTTP post XML data in C#