11
respostas

Erro No such file or directory ao gravar no S3

Estou com dificuldade de entender porque teria que alterar o código pra gravar img através da aplicação. Isso faria com que o codigo ficasse acoplado ao servidor da Amazon.

Como a aplicacao roda num servidor tomcat no meu computador sem precisar fazer qualquer alteraçao no codigo especifico pra este servidor, achei que gerando o arquivo war, poderia rodar em qualquer outro servidor web sem precisar alterar o código fonte.

Esse é o único jeito de gravar imagem no s3, ou é apenas um exemplo?

A parte do código que grava é esse:

public void loadFile(byte[] foto, String id, String url)  {
        try {
            System.out.println("Passei por dentro do Try!!!");
            Path path = Paths.get(url + id + ".jpg");
            Files.deleteIfExists(path); 
            InputStream initialStream = new ByteArrayInputStream(foto);
            byte[] buffer = new byte[initialStream.available()];
            initialStream.read(buffer);
            File targetFile = new File(url + id + ".jpg");
            OutputStream outStream = new FileOutputStream(targetFile);
            outStream.write(buffer);
            outStream.close();
        } catch (IOException e) {
            System.out.println("Passei pelo erro antes...");
            e.printStackTrace();
            System.out.println("Passei pelo erro depois...");
            System.out.println("Impressao do StackTrace do erro de envio de email: "+e.toString());
        }
    }

O erro que está dando é esse:

/var/log/httpd/elasticbeanstalk-error_log
-------------------------------------
[Fri Feb 02 15:10:44 2018] [error] (111)Connection refused: proxy: HTTP: attempt to connect to 127.0.0.1:8080 (localhost) failed
[Fri Feb 02 15:10:44 2018] [error] ap_proxy_connect_backend disabling worker for (localhost)



-------------------------------------
/var/log/tomcat8/catalina.out
-------------------------------------
Passei pelo erro depois...
Impressao do StackTrace do erro de envio de email: java.io.FileNotFoundException: http:/oscar105.com/resources/img/email/1.jpg (No such file or directory)

Presumo que seja por causa credencial, mas nao queria alterar o codigo, so inserir a credencia, tem jeito?

11 respostas

Olá,

Para acessar o Bucket criado na Amazon do seu computador local seria necessário possuir as credenciais de acesso mesmo, pois se não qualquer pessoa poderia teoricamente acessar o Bucket e inserir qualquer arquivo.

Para não utilizar as senhas diretamente no código, muitas vezes utiliza-se tais valores como variáveis de ambiente e tais variáveis são buscados por nossa aplicação.

Olá Rafael, obrigado por responde.

Depois que fiz a pergunta, percebi a aplicacao estava hospedada no computador do professor, não é o meu caso.

O meu codigo está o Ec2, que foi criado ao gerar um pacote ElasticBeanstalk.

A apricacao consegue exibir o arquivo que inseri manualmene na pasta img, antes de subir-la, mas quando tento gravar um novo arquivo pela aplicaao depois que a subi, da o erro mostrado no post

Ja faz uma semana que estou tentando descobrir o motivo, essa video aula da alura me pareceu a soluçao, mas parece que nao se aplica ao meu caso, pois minha aplicacao ja esta hospedada no EC2 instanciado pelo ElasticBeansTalk. O S3 tambem foi instanciado automaticamente nesse processo.

Voce teria uma sugestao pra eu solucionar o problema?

Jonas

Olá Jonas,

Para ser sincero, não cheguei a utilizar o Elastic Beanstalk da Amazon, mas mesmo você rodando sua aplicação na Amazon, você precisa de permissões para acessar outro serviço (no caso o S3). Uma sugestão, tenta só configurar um usuário com Full Access para o S3 e utilize tais credenciais para poder criar o seu objeto do tipo Amazon S3 para conseguir assim enviar a imagem ao Bucket, algo como:

AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
amazonS3Client = new AmazonS3Client(credentials);

Olá Rrafael, bom dia.

Tipo isso:

    public void loadFile(byte[] foto, String id, String url)  {
        try {

            AWSCredentials credentials = new BasicAWSCredentials("xxx","xxxx");
            AmazonS3Client amazonS3Client = new AmazonS3Client(credentials);

            Path path = Paths.get(url + id + ".jpg");
            Files.deleteIfExists(path); 
            InputStream initialStream = new ByteArrayInputStream(foto);
            byte[] buffer = new byte[initialStream.available()];
            initialStream.read(buffer);
            File targetFile = new File(url + id + ".jpg");
            OutputStream outStream = new FileOutputStream(targetFile);
            outStream.write(buffer);
            outStream.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Impressao do StackTrace do erro de envio de email: "+e.toString());
        }
    }

Não captei ainda, como eu vinculo a aplicacao ao objeto amazonS3Client?

Att.:

jonas

Olá Jonas,

Exato, mas no caso você precisaria chamar os métodos do AmazonS3 para poder enviar a imagem para o Bucket, parecido com o que fiz no curso:

 amazonS3.putObject(
new PutObjectRequest([Nome do Bucket], 
[Nome do arquivo] ,new ByteArrayInputStream(foto),null)
.withCannedAcl(CannedAccessControlList.PublicRead));
}

Abs

Oi Rafael, eu sequi esse tutorial , que é parecido com o que voce ensinou no video, http://blog.caelum.com.br/comece-a-trabalhar-com-java-no-amazon-s3/, e fiz asim, mas nao deu certo:

    public void loadFile(byte[] foto, String id, String url)  {
        try {

            AWSCredentials credentials = new BasicAWSCredentials("xx","xx");
            AmazonS3Client amazonS3Client = new AmazonS3Client(credentials);
            AmazonS3 s3 = new AmazonS3Client(credentials);



            Path path = Paths.get(url + id + ".jpg");
            Files.deleteIfExists(path); 


            InputStream initialStream = new ByteArrayInputStream(foto);
            byte[] buffer = new byte[initialStream.available()];
            initialStream.read(buffer);
            File targetFile = new File(url + id + ".jpg");

            s3.putObject(new PutObjectRequest("elasticbeanstalk-sa-east-1-102555465124856", id + ".jpg", new File(url + id + ".jpg")));

/*            OutputStream outStream = new FileOutputStream(targetFile);
            outStream.write(buffer);
            outStream.close();*/
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Impressao do StackTrace do erro de envio de email: "+e.toString());
        }
    }
}

Veja que comentei o codigo java que gravava pela api AmazonS3.

AmazonS3 s3 = new AmazonS3Client(credentials);
            s3.putObject(new PutObjectRequest("elasticbeanstalk-sa-east-1-102555464856", id + ".jpg", new File(url + id + ".jpg")));

    s3.putObject(new PutObjectRequest("elasticbeanstalk-sa-east-1-102555464856", id + ".jpg", new File(url + id + ".jpg")));

Ao executar, ele deu esse erro:

 com.amazonaws.SdkClientException: Unable to calculate MD5 hash: http:/oscar105.com/resources/img/email/1.jpg (No such file or directory)

Acho que o motivo do prolema é que o objeto que eu quero gravar, ficar dentro do arquivo war, gerado no deploy. Ele é uma estrutura padrao maven.

Se eu copiar e colar o recurso no browser, ele visualiza: http://oscar105.com/resources/img/email/1.jpg

Você concorda que o motivo poderia ser esse? Se sim, como eu deveria fazer?

ATt.:

jonas

Olá Jonas,

Acredito que o problema seja criar um arquivo local temporário e depois realizar a leitura para fazer o upload para o S3. Não sei ao certo o motivo, mas nas instâncias do EC2 não conseguimos fazer esse tipo de configuração.

Ao invés de utilizar o arquivo temporário, tente trabalhar com o objeto do tipo MultipartFile e depois chamar o método getInputStream(), utilizando esse construtor como no vídeo:

public PutObjectRequest(String bucketName,
                        String key,
                        InputStream input,
                        ObjectMetadata metadata)

Abs

Bom dia Rafael, gostaria de resetar esse post, e começar de novo. Como estou aprendendo a programar, é a primeira vez que estou tendo que gravar um arquivo numa pasta. Então não estou entendo direito a origem do problema. Vi em foruns que tem gente tendo problemas parecido devido ao TomCat.

Entao, acho que eu deveria entender o conceito, antes de solucionar o problema.

Estava complicado entender porque rodando no tomcat no meu computador dava certo, e ao subir pro AWS, tomcat, nao dava, Nao descobri porque nao da certo no AWS, mas descobri porque da no meu computador. No meu comptuador, eu tenho acesso ao url absoluta do arquivo war, entao, a aplicacao estava acessando, de certo modo, a pasta dentro do meu hd, ignorando que ela esteja no tomcat

Acho que o segredo pra eu solucionar o problema, é descobrir como fazer a mesma coisa, no s3.Falando de outra forma, o segredo nao é gravar no S3, o segredo é descobrir como gravar um arquivo numa pasta dentro de um arquivo WAR numa servidor TOMCAT, se eu descobrir isso, acredito que nao seria o S3 o problema.

Visto isso, teria alguma sugestao que me ajude.

Att.:

Jonas

Olá Jonas,

Pelo que já testei na Amazon, não conheço uma forma que seja possível configurar um arquivo para ser salvo localmente no Tomcat da instância, somente no S3 mesmo. Nesse link do fórum da AWS os usuários comentam brevemente sobre essa questão de segurança: https://forums.aws.amazon.com/thread.jspa?threadID=222278

Estive pesquisando na documentação da Amazon, e encontrei esse link com um código exemplo de como os arquivos podem ser salvos https://docs.aws.amazon.com/pt_br/AmazonS3/latest/dev/UploadObjSingleOpJava.html

Abs

Olá Rafael, bom dia. Obrigado mais uma vez pela resposta.

A medida que vou estudando pra resolver o problema, estou entendendo melhor a situação.

Na verdade nem seria apropriado fazer o que eu achava que tinha que fazer, gravar direto dentro do servidor que está o tomcat, no caso, o ec2.

O ec2 foi feito pra rodar a aplicacao, Pra armazenar, ou é o RDS, ou o S3, e que tem muito fundamento, se pra gravar dados em geral, eu preciso de um servidor pro banco de dador, no caso, o RDS, porque então, pra imagem, que nao deixa de ser dados, teria que ser diferente.

Meu erro era achar que pra gravar imagem não precisaria criar um repositório pra ela, como o RDS, pro banco de dados. Que só gravando a imagem no resources/img, dentro do webapp, ja seria o suficiente. Pelo que entendi até agora, eu estava querendo fazer algo que nem deve ser feito.

Criei entao um bucket no s3, pra ficar como repositório das imagens. Já aprendi gravar e deletar com a api AmazonS3.

Mas agora apareceu outro probleminha, eu preciso comparar a imagem do S3, com a imagem do BD, se ela for igual, eu não altero, se for diferente, tenho que alterar.

byte[]  foto; 

 S3Object object = s3.getObject("oscar105-img-email",  id+".jpg");
                 S3ObjectInputStream fotoS3 = object.getObjectContent();

                if(!fotoS3.equals(foto)){

      s3.deleteObject("oscar105-img-email", id + ".jpg");
                      s3.putObject("oscar105-img-email", id+".jpg", new ByteArrayInputStream(foto), null);

Eu quero converter o s3.getObjetc para Byte[], para comparar com a foto que vem do bd, que é um array de bayte ,do jeito que fiz mostrado no codigo acima. Achei a classe S3ObjectInputStream , faria essa conversão, mas não faz, preciso de uma sugestão de qual faz, ou como faz?

Att.:

Jonas

Consegui resolver a questão acima, desta forma:

S3Object object = s3.getObject("oscar105-img-email",  id+".jpg");
                 byte[] fotoS3 = IOUtils.toByteArray(object.getObjectContent());

                if(!Arrays.equals(fotoS3, foto)){
                      s3.deleteObject("oscar105-img-email", id + ".jpg");
                      s3.putObject("oscar105-img-email", id+".jpg", new ByteArrayInputStream(foto), null);
                }