fix: upload skin

This commit is contained in:
huanghongxun
2020-06-01 11:59:21 +08:00
parent bfb55a3813
commit 5805914ebb
4 changed files with 49 additions and 13 deletions

View File

@@ -33,6 +33,8 @@ import org.jackhuang.hmcl.auth.offline.OfflineAccount;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.game.TexturesLoader; import org.jackhuang.hmcl.game.TexturesLoader;
import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.DialogController; import org.jackhuang.hmcl.ui.DialogController;
import org.jackhuang.hmcl.ui.construct.PromptDialogPane; import org.jackhuang.hmcl.ui.construct.PromptDialogPane;
@@ -82,9 +84,9 @@ public class AccountListItem extends RadioButton {
return new AccountListItemSkin(this); return new AccountListItemSkin(this);
} }
public void refresh() { private Task<?> refreshAsync() {
account.clearCache(); return Task.runAsync(() -> {
thread(() -> { account.clearCache();
try { try {
account.logIn(); account.logIn();
} catch (CredentialExpiredException e) { } catch (CredentialExpiredException e) {
@@ -94,13 +96,19 @@ public class AccountListItem extends RadioButton {
// ignore cancellation // ignore cancellation
} catch (Exception e1) { } catch (Exception e1) {
LOG.log(Level.WARNING, "Failed to refresh " + account + " with password", e1); LOG.log(Level.WARNING, "Failed to refresh " + account + " with password", e1);
throw e1;
} }
} catch (AuthenticationException e) { } catch (AuthenticationException e) {
LOG.log(Level.WARNING, "Failed to refresh " + account + " with token", e); LOG.log(Level.WARNING, "Failed to refresh " + account + " with token", e);
throw e;
} }
}); });
} }
public void refresh() {
refreshAsync().whenComplete(e -> {}).start();
}
public boolean canUploadSkin() { public boolean canUploadSkin() {
return account instanceof YggdrasilAccount && !(account instanceof AuthlibInjectorAccount); return account instanceof YggdrasilAccount && !(account instanceof AuthlibInjectorAccount);
} }
@@ -121,12 +129,15 @@ public class AccountListItem extends RadioButton {
Controllers.prompt(new PromptDialogPane.Builder(i18n("account.skin.upload"), (questions, resolve, reject) -> { Controllers.prompt(new PromptDialogPane.Builder(i18n("account.skin.upload"), (questions, resolve, reject) -> {
PromptDialogPane.Builder.CandidatesQuestion q = (PromptDialogPane.Builder.CandidatesQuestion) questions.get(0); PromptDialogPane.Builder.CandidatesQuestion q = (PromptDialogPane.Builder.CandidatesQuestion) questions.get(0);
String model = q.getValue() == 0 ? "" : "slim"; String model = q.getValue() == 0 ? "" : "slim";
try { refreshAsync()
((YggdrasilAccount) account).uploadSkin(model, selectedFile.toPath()); .thenRunAsync(() -> ((YggdrasilAccount) account).uploadSkin(model, selectedFile.toPath()))
resolve.run(); .thenComposeAsync(this::refreshAsync)
} catch (AuthenticationException e) { .thenRunAsync(Schedulers.javafx(), resolve::run)
reject.accept(AddAccountPane.accountException(e)); .whenComplete(Schedulers.javafx(), e -> {
} if (e != null) {
reject.accept(AddAccountPane.accountException(e));
}
}).start();
}).addQuestion(new PromptDialogPane.Builder.CandidatesQuestion(i18n("account.skin.model"), }).addQuestion(new PromptDialogPane.Builder.CandidatesQuestion(i18n("account.skin.model"),
i18n("account.skin.model.default"), i18n("account.skin.model.slim")))); i18n("account.skin.model.default"), i18n("account.skin.model.slim"))));
} }

View File

@@ -191,7 +191,7 @@ public class YggdrasilAccount extends Account {
} }
public void uploadSkin(String model, Path file) throws AuthenticationException, UnsupportedOperationException { public void uploadSkin(String model, Path file) throws AuthenticationException, UnsupportedOperationException {
service.uploadSkin(characterUUID, model, file); service.uploadSkin(characterUUID, session.getAccessToken(), model, file);
} }
private static String randomClientToken() { private static String randomClientToken() {

View File

@@ -28,6 +28,7 @@ import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
import org.jackhuang.hmcl.util.gson.ValidationTypeAdapterFactory; import org.jackhuang.hmcl.util.gson.ValidationTypeAdapterFactory;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.HttpMultipartRequest; import org.jackhuang.hmcl.util.io.HttpMultipartRequest;
import org.jackhuang.hmcl.util.io.IOUtils;
import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.jackhuang.hmcl.util.javafx.ObservableOptionalCache; import org.jackhuang.hmcl.util.javafx.ObservableOptionalCache;
@@ -151,16 +152,21 @@ public class YggdrasilService {
requireEmpty(request(provider.getInvalidationURL(), createRequestWithCredentials(accessToken, clientToken))); requireEmpty(request(provider.getInvalidationURL(), createRequestWithCredentials(accessToken, clientToken)));
} }
public void uploadSkin(UUID uuid, String model, Path file) throws AuthenticationException, UnsupportedOperationException { public void uploadSkin(UUID uuid, String accessToken, String model, Path file) throws AuthenticationException, UnsupportedOperationException {
try { try {
HttpURLConnection con = NetworkUtils.createHttpConnection(provider.getSkinUploadURL(uuid)); HttpURLConnection con = NetworkUtils.createHttpConnection(provider.getSkinUploadURL(uuid));
con.setRequestMethod("PUT"); con.setRequestMethod("PUT");
con.setRequestProperty("Authorization", "Bearer " + accessToken);
con.setDoOutput(true); con.setDoOutput(true);
try (HttpMultipartRequest request = new HttpMultipartRequest(con)) { try (HttpMultipartRequest request = new HttpMultipartRequest(con)) {
request.param("model", model);
try (InputStream fis = Files.newInputStream(file)) { try (InputStream fis = Files.newInputStream(file)) {
request.file("file", FileUtils.getName(file), "image/" + FileUtils.getExtension(file), fis); request.file("file", FileUtils.getName(file), "image/" + FileUtils.getExtension(file), fis);
} }
request.param("model", model); }
String response = IOUtils.readFullyAsString(con.getInputStream());
if (response.startsWith("{")) {
handleErrorMessage(fromJson(response, ErrorResponse.class));
} }
} catch (IOException e) { } catch (IOException e) {
throw new AuthenticationException(e); throw new AuthenticationException(e);

View File

@@ -1,3 +1,20 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.util.io; package org.jackhuang.hmcl.util.io;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@@ -17,6 +34,8 @@ public class HttpMultipartRequest implements Closeable {
public HttpMultipartRequest(HttpURLConnection urlConnection) throws IOException { public HttpMultipartRequest(HttpURLConnection urlConnection) throws IOException {
this.urlConnection = urlConnection; this.urlConnection = urlConnection;
urlConnection.setDoOutput(true);
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); urlConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
stream = new ByteArrayOutputStream(); stream = new ByteArrayOutputStream();
@@ -31,9 +50,9 @@ public class HttpMultipartRequest implements Closeable {
addLine("--" + boundary); addLine("--" + boundary);
addLine(String.format("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"", name, filename)); addLine(String.format("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"", name, filename));
addLine("Content-Type: " + contentType); addLine("Content-Type: " + contentType);
addLine("Content-Transfer-Encoding: binary");
addLine(""); addLine("");
IOUtils.copyTo(inputStream, stream); IOUtils.copyTo(inputStream, stream);
addLine("");
return this; return this;
} }