Upload files to WS via the REST API

Dear developers,

I am trying to upload a wsxz file back to WS using the API. I use the following code:

var client = new HttpClient();
client.BaseAddress = new Uri(srvAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));

Byte[] fileBytes = File.ReadAllBytes(wsxzFilePath);

HttpContent fileStreamContent = new ByteArrayContent(fileBytes);
var response = client.PostAsync(" /ws-api/v1/files?token=" + token, fileStreamContent).Result;

The response I am receiving is 406-Not acceptable.

What do I miss in here?

Thanks in advance for your support.

Regards,

Laurent

Parents
  • The request must have Content-Type=multipart/form-data and the attribute "file" that contains the file.

    The best way to get more information than presented in the documentation is, if you have access to a WS instance to inspect the interactions that the ui makes with the api (Developer Tools > Network in Chrome).

    In Java, using Spring it would look something like:
    public VMFile upload(Resource fileContent) throws RestAPIException {

    MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
    parts.add("file", fileContent);


    ResponseEntity<VMFile> response = restTemplate.exchange(url, HttpMethod.POST,
    new HttpEntity<>(parts, context.getMultiPartHeaders()),
    new ParameterizedTypeReference<VMFile>() {
    });

    return response.getBody();
    }

    public HttpHeaders getMultiPartHeaders() {
    return new HttpHeaders() {
    {
    setContentType(MediaType.MULTIPART_FORM_DATA);
    setAccept(new ArrayList<>(Arrays.asList(MediaType.APPLICATION_JSON)));
    set("Token", token);
    }
    };
    }
Reply
  • The request must have Content-Type=multipart/form-data and the attribute "file" that contains the file.

    The best way to get more information than presented in the documentation is, if you have access to a WS instance to inspect the interactions that the ui makes with the api (Developer Tools > Network in Chrome).

    In Java, using Spring it would look something like:
    public VMFile upload(Resource fileContent) throws RestAPIException {

    MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
    parts.add("file", fileContent);


    ResponseEntity<VMFile> response = restTemplate.exchange(url, HttpMethod.POST,
    new HttpEntity<>(parts, context.getMultiPartHeaders()),
    new ParameterizedTypeReference<VMFile>() {
    });

    return response.getBody();
    }

    public HttpHeaders getMultiPartHeaders() {
    return new HttpHeaders() {
    {
    setContentType(MediaType.MULTIPART_FORM_DATA);
    setAccept(new ArrayList<>(Arrays.asList(MediaType.APPLICATION_JSON)));
    set("Token", token);
    }
    };
    }
Children
  • Dear Stefan,

    Thank you for your quick reply.  Based on your JAVA code and searches on the internet, I managed to move forward but I am still not successful. At least my POST method runs to completion but it returns an error message:

    {"errors":[{"code":401,"type":"UNAUTHORIZED","message":"No session token provided."}]}

    Based on inspections I made, I suspect that the boundary is the reason of the problem but I can't find the reason why. All this is new land to me and I made a lot of tries before contacting you.

    Have you any hint/idea about the reason of the problem. Below is my code.

    Thanks in advance for your support.

    Regards,

    Laurent

    var client = new HttpClient();

    client.BaseAddress = new Uri(srvAddress);

    client.DefaultRequestHeaders.Accept.Clear();

    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    string boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");

    Stream rs = new MemoryStream();

    var endBoundaryBytes = System.Text.Encoding.UTF8.GetBytes("\r\n--" + boundary + "--");

    string headerTemplate = "--" + boundary + "\r\nContent-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";

    string header = string.Format(headerTemplate, "file", fileName, "application/octet-stream");

    byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);

    rs.Write(headerbytes, 0, headerbytes.Length);

    using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read))

    {

    var buffer = new byte[1024];

    var bytesRead = 0;

    while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)

    {

    rs.Write(buffer, 0, bytesRead);

    }

    }

    rs.Write(endBoundaryBytes, 0, endBoundaryBytes.Length);

    rs.Flush();

    rs.Position = 0;

    byte[] tempBuffer = new byte[rs.Length];

    rs.Read(tempBuffer, 0, tempBuffer.Length);

    rs.Close();

    MultipartFormDataContent formData = new MultipartFormDataContent();

    formData.Headers.Remove("Content-Type");

    formData.Headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary);

    ByteArrayContent bac = new ByteArrayContent(tempBuffer);

    formData.Add(bac);

    HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, " /ws-api/v1/files?token=" + token);

    req.Content = formData;

    HttpResponseMessage response = await client.SendAsync(req);

    var jsonResponse = await response.Content.ReadAsStringAsync();

  • I see that your token is already in the url, so it should be ok. The only way to get that error when you set the Token in the url or headers is that the token is empty.

    Can you intercept the HTTP request that is sent to the server? (you can use Membrane Monitor for this) That would give us a better image of the request.
  • I was adding the boundary at the wrong location. Boundary should be added at the Multipartformdatacontent level. This now works:

    MultipartFormDataContent formData = new MultipartFormDataContent(boundary);

    formData.Headers.Remove("Content-Type");

    formData.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=" + boundary);

    ByteArrayContent bac = new ByteArrayContent(tempBuffer);

    bac.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");

    bac.Headers.ContentDisposition.Name = "\"file\"";

    bac.Headers.ContentDisposition.FileName = "\"" + file + "\"";

    bac.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

     

    formData.Add(bac);

     

    HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, " /ws-api/v1/files?token=" + token);

    req.Content = formData;

  • Laurent Pierret (lpierret) This is the different question.

    How can I connect to the world server?

    I'm trying the rest api using my local server with appropriate request url, body and headers.
    Here it is
    localhost:8010/.../login

    Header: content-type: application/json

    RequestBody:
    {
    "username":"swathiperu",
    "password":"password"
    }

    Giving me 404 error. Can you please help me on this.
  • Dear Swathi,

    Please have a look at this thread, which I posted when I was starting working with REST API:

    community.sdl.com/.../7630

    I hope this helps.

    Regards,
    Laurent
  • Thanks, I have a question. What is this (your server name) do I need to get the SDL server installed and run in my local ? I'm confused now. I'm looking for REST API's where third party can connect to world server and get the data.

    Thanks in advance.

    Regards,
    Swathi Peru.
  • Dear Swathi,

    "Server name" is the name of the server where your SDL WorldServer is deployed (+port 8080). Your third party product needs to know where it can find SDL WorldServer.

    Regards,

    Laurent
  • Here is a complete java code that works

    public String restUploadFile2(String sessionId, String filePath) {
    try {
    String attachmentName = "file";
    String attachmentFileName = "MyTestFile.txt";

    URL url = new URL(baseUrl + "files");
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setDoOutput(true);
    connection.setUseCaches(false);
    connection.setRequestMethod("POST");
    connection.addRequestProperty("token", sessionId);
    connection.setRequestProperty("Connection", "Keep-Alive");
    connection.setRequestProperty("Cache-Control", "no-cache");

    String boundary = "--" + UUID.randomUUID() + "--";

    MultipartEntityBuilder builder = MultipartEntityBuilder.create();
    builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
    builder.setBoundary(boundary);
    connection.addRequestProperty("content-type", "multipart/form-data; boundary=" + boundary);

    byte[] data = Files.readAllBytes(Paths.get(filePath));
    ContentBody contentPart = new ByteArrayBody(data, attachmentFileName);
    builder.addPart(attachmentName, contentPart);

    HttpEntity entity = builder.build();

    entity.writeTo(connection.getOutputStream());

    if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
    System.out.println("Status Code: " + connection.getResponseCode());
    System.out.println("Message: " + connection.getResponseMessage());
    throw new Exception(connection.getResponseMessage());
    }

    ObjectMapper mapper = new ObjectMapper();
    WSRestUploadFileResult uploadFileResult = mapper.readValue(connection.getInputStream(), WSRestUploadFileResult.class);
    return uploadFileResult.getInternalName();
    } catch (Exception e) {
    System.out.println(e.getMessage());
    }
    return null;
    }